Prefix all SX URLs with /sx/ for WhatsApp-safe sharing
All routes moved under /sx/ prefix: - / redirects to /sx/ - /sx/ serves home page - /sx/<path:expr> is the catch-all for SX expression URLs - Bare /(...) and /~... redirect to /sx/(...) and /sx/~... - All ~600 hrefs, sx-get attrs, defhandler paths, redirect targets, and blueprint routes updated across 44 files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
30
sx/app.py
30
sx/app.py
@@ -119,15 +119,23 @@ def create_app() -> "Quart":
|
|||||||
|
|
||||||
@app.before_request
|
@app.before_request
|
||||||
async def sx_url_redirect():
|
async def sx_url_redirect():
|
||||||
"""Redirect old-style paths to SX expression URLs (301)."""
|
"""Redirect old-style paths and bare root to /sx/ URLs (301)."""
|
||||||
from quart import request, redirect as q_redirect
|
from quart import request, redirect as q_redirect
|
||||||
path = request.path
|
path = request.path
|
||||||
|
# Root → /sx/
|
||||||
|
if path == "/":
|
||||||
|
return q_redirect("/sx/", 301)
|
||||||
# Skip non-page paths
|
# Skip non-page paths
|
||||||
if path.startswith(("/static/", "/internal/", "/auth/", "/sx/")):
|
if path.startswith(("/static/", "/internal/", "/auth/")):
|
||||||
return None
|
return None
|
||||||
# Skip SX expression URLs (already in new format)
|
# Skip SX expression URLs (already in new format under /sx/)
|
||||||
|
if path.startswith("/sx/"):
|
||||||
|
return None
|
||||||
|
# Redirect bare /(...) to /sx/(...)
|
||||||
if path.startswith("/(") or path.startswith("/~"):
|
if path.startswith("/(") or path.startswith("/~"):
|
||||||
return None
|
qs = request.query_string.decode()
|
||||||
|
target = f"/sx{path}" + ("?" + qs if qs else "")
|
||||||
|
return q_redirect(target, 301)
|
||||||
new_url = redirect_old_url(path)
|
new_url = redirect_old_url(path)
|
||||||
if new_url:
|
if new_url:
|
||||||
qs = request.query_string.decode()
|
qs = request.query_string.decode()
|
||||||
@@ -140,20 +148,26 @@ def create_app() -> "Quart":
|
|||||||
from quart import request, redirect
|
from quart import request, redirect
|
||||||
path = request.path
|
path = request.path
|
||||||
# Skip SX expression URLs — they don't use trailing slashes
|
# Skip SX expression URLs — they don't use trailing slashes
|
||||||
if "(" in path or path.startswith("/~"):
|
if "(" in path or "/~" in path:
|
||||||
return None
|
return None
|
||||||
if (path != "/"
|
if (path != "/"
|
||||||
|
and path != "/sx/"
|
||||||
and not path.endswith("/")
|
and not path.endswith("/")
|
||||||
and request.method == "GET"
|
and request.method == "GET"
|
||||||
and not path.startswith(("/static/", "/internal/", "/auth/"))
|
and not path.startswith(("/static/", "/internal/", "/auth/", "/sx/"))
|
||||||
and "." not in path.rsplit("/", 1)[-1]):
|
and "." not in path.rsplit("/", 1)[-1]):
|
||||||
qs = request.query_string.decode()
|
qs = request.query_string.decode()
|
||||||
target = path + "/" + ("?" + qs if qs else "")
|
target = path + "/" + ("?" + qs if qs else "")
|
||||||
return redirect(target, 301)
|
return redirect(target, 301)
|
||||||
|
|
||||||
@app.get("/<path:expr>")
|
@app.get("/sx/")
|
||||||
|
async def sx_home():
|
||||||
|
"""SX docs home page."""
|
||||||
|
return await eval_sx_url("/")
|
||||||
|
|
||||||
|
@app.get("/sx/<path:expr>")
|
||||||
async def sx_eval_route(expr):
|
async def sx_eval_route(expr):
|
||||||
"""Catch-all: evaluate SX expression URLs."""
|
"""Catch-all: evaluate SX expression URLs under /sx/ prefix."""
|
||||||
result = await eval_sx_url(f"/{expr}")
|
result = await eval_sx_url(f"/{expr}")
|
||||||
if result is None:
|
if result is None:
|
||||||
from quart import abort
|
from quart import abort
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ def register(url_prefix: str = "/") -> Blueprint:
|
|||||||
# SSE stays in Python — fundamentally different paradigm (async generator).
|
# SSE stays in Python — fundamentally different paradigm (async generator).
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
@bp.get("/(geography.(hypermedia.(reference.(api.sse-time))))")
|
@bp.get("/sx/(geography.(hypermedia.(reference.(api.sse-time))))")
|
||||||
async def ref_sse_time():
|
async def ref_sse_time():
|
||||||
async def generate():
|
async def generate():
|
||||||
for _ in range(30): # stream for 60 seconds max
|
for _ in range(30): # stream for 60 seconds max
|
||||||
@@ -38,7 +38,7 @@ def register(url_prefix: str = "/") -> Blueprint:
|
|||||||
|
|
||||||
_marsh_sale_idx = {"n": 0}
|
_marsh_sale_idx = {"n": 0}
|
||||||
|
|
||||||
@bp.get("/(geography.(reactive.(api.flash-sale)))")
|
@bp.get("/sx/(geography.(reactive.(api.flash-sale)))")
|
||||||
async def api_marsh_flash_sale():
|
async def api_marsh_flash_sale():
|
||||||
from shared.sx.helpers import sx_response
|
from shared.sx.helpers import sx_response
|
||||||
prices = [14.99, 9.99, 24.99, 12.49, 7.99, 29.99, 4.99, 16.50]
|
prices = [14.99, 9.99, 24.99, 12.49, 7.99, 29.99, 4.99, 16.50]
|
||||||
@@ -60,7 +60,7 @@ def register(url_prefix: str = "/") -> Blueprint:
|
|||||||
|
|
||||||
_settle_counter = {"n": 0}
|
_settle_counter = {"n": 0}
|
||||||
|
|
||||||
@bp.get("/(geography.(reactive.(api.settle-data)))")
|
@bp.get("/sx/(geography.(reactive.(api.settle-data)))")
|
||||||
async def api_settle_data():
|
async def api_settle_data():
|
||||||
from shared.sx.helpers import sx_response
|
from shared.sx.helpers import sx_response
|
||||||
_settle_counter["n"] += 1
|
_settle_counter["n"] += 1
|
||||||
@@ -76,7 +76,7 @@ def register(url_prefix: str = "/") -> Blueprint:
|
|||||||
|
|
||||||
# --- Demo 4: signal-bound URL endpoints ---
|
# --- Demo 4: signal-bound URL endpoints ---
|
||||||
|
|
||||||
@bp.get("/(geography.(reactive.(api.search-products)))")
|
@bp.get("/sx/(geography.(reactive.(api.search-products)))")
|
||||||
async def api_search_products():
|
async def api_search_products():
|
||||||
from shared.sx.helpers import sx_response
|
from shared.sx.helpers import sx_response
|
||||||
q = request.args.get("q", "")
|
q = request.args.get("q", "")
|
||||||
@@ -95,7 +95,7 @@ def register(url_prefix: str = "/") -> Blueprint:
|
|||||||
)
|
)
|
||||||
return sx_response(sx_src)
|
return sx_response(sx_src)
|
||||||
|
|
||||||
@bp.get("/(geography.(reactive.(api.search-events)))")
|
@bp.get("/sx/(geography.(reactive.(api.search-events)))")
|
||||||
async def api_search_events():
|
async def api_search_events():
|
||||||
from shared.sx.helpers import sx_response
|
from shared.sx.helpers import sx_response
|
||||||
q = request.args.get("q", "")
|
q = request.args.get("q", "")
|
||||||
@@ -114,7 +114,7 @@ def register(url_prefix: str = "/") -> Blueprint:
|
|||||||
)
|
)
|
||||||
return sx_response(sx_src)
|
return sx_response(sx_src)
|
||||||
|
|
||||||
@bp.get("/(geography.(reactive.(api.search-posts)))")
|
@bp.get("/sx/(geography.(reactive.(api.search-posts)))")
|
||||||
async def api_search_posts():
|
async def api_search_posts():
|
||||||
from shared.sx.helpers import sx_response
|
from shared.sx.helpers import sx_response
|
||||||
q = request.args.get("q", "")
|
q = request.args.get("q", "")
|
||||||
@@ -135,7 +135,7 @@ def register(url_prefix: str = "/") -> Blueprint:
|
|||||||
|
|
||||||
# --- Demo 5: marsh transform endpoint ---
|
# --- Demo 5: marsh transform endpoint ---
|
||||||
|
|
||||||
@bp.get("/(geography.(reactive.(api.catalog)))")
|
@bp.get("/sx/(geography.(reactive.(api.catalog)))")
|
||||||
async def api_catalog():
|
async def api_catalog():
|
||||||
from shared.sx.helpers import sx_response
|
from shared.sx.helpers import sx_response
|
||||||
items = [
|
items = [
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
"Each bar shows how many of the "
|
"Each bar shows how many of the "
|
||||||
(strong (str total-components))
|
(strong (str total-components))
|
||||||
" total components a page actually needs, computed by the "
|
" total components a page actually needs, computed by the "
|
||||||
(a :href "/(language.(spec.deps))" :class "text-violet-700 underline" "deps.sx")
|
(a :href "/sx/(language.(spec.deps))" :class "text-violet-700 underline" "deps.sx")
|
||||||
" transitive closure algorithm. "
|
" transitive closure algorithm. "
|
||||||
"Click a page to see its component tree; expand a component to see its SX source.")
|
"Click a page to see its component tree; expand a component to see its SX source.")
|
||||||
|
|
||||||
|
|||||||
@@ -373,7 +373,7 @@
|
|||||||
"directly. They arrive as part of the rendered HTML — keyframes, custom properties, "
|
"directly. They arrive as part of the rendered HTML — keyframes, custom properties, "
|
||||||
"scoped rules, anything.")
|
"scoped rules, anything.")
|
||||||
(li (strong "External stylesheets: ") "Components can reference pre-loaded CSS files "
|
(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 "/sx/(applications.(cssx.async))" "Async CSS") ").")
|
||||||
(li (strong "Custom properties: ") "A " (code "~theme") " component sets "
|
(li (strong "Custom properties: ") "A " (code "~theme") " component sets "
|
||||||
(code "--color-primary") " etc. via a " (code "<style>") " block. Everything "
|
(code "--color-primary") " etc. via a " (code "<style>") " block. Everything "
|
||||||
"using " (code "var()") " repaints automatically."))
|
"using " (code "var()") " repaints automatically."))
|
||||||
|
|||||||
@@ -79,7 +79,7 @@
|
|||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
"Special forms are syntactic constructs whose arguments are NOT evaluated before dispatch. Each form has its own evaluation rules — unlike primitives, which receive pre-evaluated values. Together with primitives, special forms define the complete language surface.")
|
"Special forms are syntactic constructs whose arguments are NOT evaluated before dispatch. Each form has its own evaluation rules — unlike primitives, which receive pre-evaluated values. Together with primitives, special forms define the complete language surface.")
|
||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
"Forms marked with a tail position enable " (a :href "/(etc.(essay.tail-call-optimization))" :class "text-violet-600 hover:underline" "tail-call optimization") " — recursive calls in tail position use constant stack space.")
|
"Forms marked with a tail position enable " (a :href "/sx/(etc.(essay.tail-call-optimization))" :class "text-violet-600 hover:underline" "tail-call optimization") " — recursive calls in tail position use constant stack space.")
|
||||||
(div :class "space-y-10" forms))))
|
(div :class "space-y-10" forms))))
|
||||||
|
|
||||||
(defcomp ~docs-server-rendering-content ()
|
(defcomp ~docs-server-rendering-content ()
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -9,7 +9,7 @@
|
|||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
"The property these systems lack has a name: " (a :href "https://en.wikipedia.org/wiki/Reflection_(computer_programming)" :class "text-violet-600 hover:underline" "reflexivity") ". A reflexive system can represent itself, reason about its own structure, and modify itself based on that reasoning. " (a :href "https://en.wikipedia.org/wiki/Lisp_(programming_language)" :class "text-violet-600 hover:underline" "Lisp") " has had this property " (a :href "https://en.wikipedia.org/wiki/Lisp_(programming_language)#History" :class "text-violet-600 hover:underline" "since 1958") ". The web has never had it.")
|
"The property these systems lack has a name: " (a :href "https://en.wikipedia.org/wiki/Reflection_(computer_programming)" :class "text-violet-600 hover:underline" "reflexivity") ". A reflexive system can represent itself, reason about its own structure, and modify itself based on that reasoning. " (a :href "https://en.wikipedia.org/wiki/Lisp_(programming_language)" :class "text-violet-600 hover:underline" "Lisp") " has had this property " (a :href "https://en.wikipedia.org/wiki/Lisp_(programming_language)#History" :class "text-violet-600 hover:underline" "since 1958") ". The web has never had it.")
|
||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
"SX is a complete Lisp. It has " (a :href "https://en.wikipedia.org/wiki/Homoiconicity" :class "text-violet-600 hover:underline" "homoiconicity") " — code is data, data is code. It has a " (a :href "/(language.(spec.core))" :class "text-violet-600 hover:underline" "self-hosting specification") " — SX defined in SX. It has " (code "eval") " and " (code "quote") " and " (a :href "https://en.wikipedia.org/wiki/Macro_(computer_science)#Syntactic_macros" :class "text-violet-600 hover:underline" "macros") ". And it runs on the wire — the format that travels between server and client IS the language. This combination has consequences."))
|
"SX is a complete Lisp. It has " (a :href "https://en.wikipedia.org/wiki/Homoiconicity" :class "text-violet-600 hover:underline" "homoiconicity") " — code is data, data is code. It has a " (a :href "/sx/(language.(spec.core))" :class "text-violet-600 hover:underline" "self-hosting specification") " — SX defined in SX. It has " (code "eval") " and " (code "quote") " and " (a :href "https://en.wikipedia.org/wiki/Macro_(computer_science)#Syntactic_macros" :class "text-violet-600 hover:underline" "macros") ". And it runs on the wire — the format that travels between server and client IS the language. This combination has consequences."))
|
||||||
|
|
||||||
(~doc-section :title "What homoiconicity changes" :id "homoiconicity"
|
(~doc-section :title "What homoiconicity changes" :id "homoiconicity"
|
||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
"In an SX web, the AI reads the same s-expressions the browser reads. The component definitions " (em "are") " the documentation — a " (code "defcomp") " declares its parameters, its structure, and its semantics in one expression. There is no " (a :href "https://en.wikipedia.org/wiki/OpenAPI_Specification" :class "text-violet-600 hover:underline" "Swagger spec") " describing an API. The API " (em "is") " the language, and the language is self-describing.")
|
"In an SX web, the AI reads the same s-expressions the browser reads. The component definitions " (em "are") " the documentation — a " (code "defcomp") " declares its parameters, its structure, and its semantics in one expression. There is no " (a :href "https://en.wikipedia.org/wiki/OpenAPI_Specification" :class "text-violet-600 hover:underline" "Swagger spec") " describing an API. The API " (em "is") " the language, and the language is self-describing.")
|
||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
"An AI that understands SX understands the " (a :href "/(language.(spec.core))" :class "text-violet-600 hover:underline" "spec") ". And the spec is written in SX. So the AI understands the definition of the language it is using, in the language it is using. This " (a :href "https://en.wikipedia.org/wiki/Reflexivity_(social_theory)" :class "text-violet-600 hover:underline" "reflexive") " property means the AI does not need a separate mental model for \"the web\" and \"the language\" — they are the same thing."))
|
"An AI that understands SX understands the " (a :href "/sx/(language.(spec.core))" :class "text-violet-600 hover:underline" "spec") ". And the spec is written in SX. So the AI understands the definition of the language it is using, in the language it is using. This " (a :href "https://en.wikipedia.org/wiki/Reflexivity_(social_theory)" :class "text-violet-600 hover:underline" "reflexive") " property means the AI does not need a separate mental model for \"the web\" and \"the language\" — they are the same thing."))
|
||||||
|
|
||||||
(~doc-section :title "Live system modification" :id "live-modification"
|
(~doc-section :title "Live system modification" :id "live-modification"
|
||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
"A macro is a function that takes code and returns code. An AI generating macros is writing programs that write programs. With " (code "eval") ", those generated programs can generate more programs at runtime. This is not a metaphor — it is the literal mechanism.")
|
"A macro is a function that takes code and returns code. An AI generating macros is writing programs that write programs. With " (code "eval") ", those generated programs can generate more programs at runtime. This is not a metaphor — it is the literal mechanism.")
|
||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
"The " (a :href "/(etc.(philosophy.godel-escher-bach))" :class "text-violet-600 hover:underline" "Gödel numbering") " parallel is not incidental. " (a :href "https://en.wikipedia.org/wiki/Kurt_G%C3%B6del" :class "text-violet-600 hover:underline" "Gödel") " showed that any sufficiently powerful formal system can encode statements about itself. A complete Lisp on the wire is a sufficiently powerful formal system. The web can make statements about itself — components that inspect other components, macros that rewrite the page structure, expressions that generate new expressions based on the current state of the system.")
|
"The " (a :href "/sx/(etc.(philosophy.godel-escher-bach))" :class "text-violet-600 hover:underline" "Gödel numbering") " parallel is not incidental. " (a :href "https://en.wikipedia.org/wiki/Kurt_G%C3%B6del" :class "text-violet-600 hover:underline" "Gödel") " showed that any sufficiently powerful formal system can encode statements about itself. A complete Lisp on the wire is a sufficiently powerful formal system. The web can make statements about itself — components that inspect other components, macros that rewrite the page structure, expressions that generate new expressions based on the current state of the system.")
|
||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
"Consider what this enables for AI:")
|
"Consider what this enables for AI:")
|
||||||
(ul :class "space-y-2 text-stone-600 mt-2"
|
(ul :class "space-y-2 text-stone-600 mt-2"
|
||||||
@@ -63,7 +63,7 @@
|
|||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
"The same " (a :href "https://en.wikipedia.org/wiki/Homoiconicity" :class "text-violet-600 hover:underline" "homoiconicity") " that makes this powerful makes it dangerous. Code-as-data means an AI can inject " (em "behaviour") ", not just content. A malicious expression evaluated in the wrong context could exfiltrate data, modify other components, or disrupt the system.")
|
"The same " (a :href "https://en.wikipedia.org/wiki/Homoiconicity" :class "text-violet-600 hover:underline" "homoiconicity") " that makes this powerful makes it dangerous. Code-as-data means an AI can inject " (em "behaviour") ", not just content. A malicious expression evaluated in the wrong context could exfiltrate data, modify other components, or disrupt the system.")
|
||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
"This is why the " (a :href "/(language.(spec.primitives))" :class "text-violet-600 hover:underline" "primitive set") " is the critical security boundary. The spec defines exactly which operations are available. A sandboxed evaluator that only exposes pure primitives (arithmetic, string operations, list manipulation) cannot perform I/O. Cannot access the network. Cannot modify the DOM outside its designated target. The language is " (a :href "https://en.wikipedia.org/wiki/Turing_completeness" :class "text-violet-600 hover:underline" "Turing-complete") " within the sandbox and powerless outside it.")
|
"This is why the " (a :href "/sx/(language.(spec.primitives))" :class "text-violet-600 hover:underline" "primitive set") " is the critical security boundary. The spec defines exactly which operations are available. A sandboxed evaluator that only exposes pure primitives (arithmetic, string operations, list manipulation) cannot perform I/O. Cannot access the network. Cannot modify the DOM outside its designated target. The language is " (a :href "https://en.wikipedia.org/wiki/Turing_completeness" :class "text-violet-600 hover:underline" "Turing-complete") " within the sandbox and powerless outside it.")
|
||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
"Different contexts grant different primitive sets. A component evaluated in a page slot gets rendering primitives. A macro gets code-transformation primitives. A federated expression from an untrusted node gets the minimal safe set. The sandbox is not bolted on — it is inherent in the language's architecture. What you can do depends on which primitives are in scope.")
|
"Different contexts grant different primitive sets. A component evaluated in a page slot gets rendering primitives. A macro gets code-transformation primitives. A federated expression from an untrusted node gets the minimal safe set. The sandbox is not bolted on — it is inherent in the language's architecture. What you can do depends on which primitives are in scope.")
|
||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
|
|||||||
@@ -37,11 +37,11 @@
|
|||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
"In SX, this is literal:")
|
"In SX, this is literal:")
|
||||||
(ul :class "space-y-2 text-stone-600 mt-2"
|
(ul :class "space-y-2 text-stone-600 mt-2"
|
||||||
(li (strong "A URL is an s-expression.") " " (code "/(language.(doc.introduction))") " is not an opaque string — it is a parseable, composable expression. It can be decomposed into parts, transformed by functions, and reasoned about by the same evaluator that renders pages.")
|
(li (strong "A URL is an s-expression.") " " (code "/sx/(language.(doc.introduction))") " is not an opaque string — it is a parseable, composable expression. It can be decomposed into parts, transformed by functions, and reasoned about by the same evaluator that renders pages.")
|
||||||
(li (strong "A response is an s-expression.") " What the server sends is " (code "(div :class \"p-4\" (h2 \"Hello\"))") " — the same notation as the component that produced it. The wire format is the language.")
|
(li (strong "A response is an s-expression.") " What the server sends is " (code "(div :class \"p-4\" (h2 \"Hello\"))") " — the same notation as the component that produced it. The wire format is the language.")
|
||||||
(li (strong "A component is an s-expression.") " " (code "(defcomp ~card (&key title) (div (h2 title)))") " is simultaneously a definition, a value, and data that can be inspected and transformed.")
|
(li (strong "A component is an s-expression.") " " (code "(defcomp ~card (&key title) (div (h2 title)))") " is simultaneously a definition, a value, and data that can be inspected and transformed.")
|
||||||
(li (strong "A query is an s-expression.") " The URL " (code "/(language.(spec.core))") " is a function call. The response is the return value. Routing is evaluation.")
|
(li (strong "A query is an s-expression.") " The URL " (code "/sx/(language.(spec.core))") " is a function call. The response is the return value. Routing is evaluation.")
|
||||||
(li (strong "The specification is s-expressions.") " The " (a :href "/(language.(spec.core))" :class "text-violet-600 hover:underline" "SX spec") " is written in SX. The evaluator is defined in the language it evaluates. The parser is defined in the language it parses."))
|
(li (strong "The specification is s-expressions.") " The " (a :href "/sx/(language.(spec.core))" :class "text-violet-600 hover:underline" "SX spec") " is written in SX. The evaluator is defined in the language it evaluates. The parser is defined in the language it parses."))
|
||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
"There is " (em "one") " kind of stuff. Everything is made of it. The address of a thing and the thing itself are the same kind of thing."))
|
"There is " (em "one") " kind of stuff. Everything is made of it. The address of a thing and the thing itself are the same kind of thing."))
|
||||||
|
|
||||||
@@ -50,19 +50,19 @@
|
|||||||
"When the medium is uniform, operations that were impossible become trivial:")
|
"When the medium is uniform, operations that were impossible become trivial:")
|
||||||
(ul :class "space-y-3 text-stone-600 mt-2"
|
(ul :class "space-y-3 text-stone-600 mt-2"
|
||||||
(li (strong "URLs compose.") " If " (code "(language (doc introduction))") " and " (code "(language (spec core))") " are both expressions, then " (code "(diff (language (spec signals)) (language (spec eval)))") " is a natural composition. Two queries, side by side. The URL algebra falls out of the expression algebra. You do not need to design it separately — it was always there.")
|
(li (strong "URLs compose.") " If " (code "(language (doc introduction))") " and " (code "(language (spec core))") " are both expressions, then " (code "(diff (language (spec signals)) (language (spec eval)))") " is a natural composition. Two queries, side by side. The URL algebra falls out of the expression algebra. You do not need to design it separately — it was always there.")
|
||||||
(li (strong "The site can show its own source.") " " (code "/(source.(~essay-self-defining-medium))") " returns the component definition of this essay. Not a screenshot. Not a prettified view. The actual s-expression that, when evaluated, produces what you are reading now. The page and its source code are the same kind of thing, so displaying one as the other is just evaluation.")
|
(li (strong "The site can show its own source.") " " (code "/sx/(source.(~essay-self-defining-medium))") " returns the component definition of this essay. Not a screenshot. Not a prettified view. The actual s-expression that, when evaluated, produces what you are reading now. The page and its source code are the same kind of thing, so displaying one as the other is just evaluation.")
|
||||||
(li (strong "The spec is executable documentation.") " The " (a :href "/(language.(bootstrapper.self-hosting))" :class "text-violet-600 hover:underline" "self-hosting bootstrapper") " reads the SX spec (written in SX) and produces a working evaluator. The documentation is the implementation. The implementation is the documentation. There is no drift because there is no gap.")
|
(li (strong "The spec is executable documentation.") " The " (a :href "/sx/(language.(bootstrapper.self-hosting))" :class "text-violet-600 hover:underline" "self-hosting bootstrapper") " reads the SX spec (written in SX) and produces a working evaluator. The documentation is the implementation. The implementation is the documentation. There is no drift because there is no gap.")
|
||||||
(li (strong "Inspection is free.") " " (code "/(inspect.(language.(doc.primitives)))") " can show the dependency graph, CSS footprint, and render plan of any page — because the page is data, and data can be walked, analysed, and reported on by the same system that renders it.")
|
(li (strong "Inspection is free.") " " (code "/sx/(inspect.(language.(doc.primitives)))") " can show the dependency graph, CSS footprint, and render plan of any page — because the page is data, and data can be walked, analysed, and reported on by the same system that renders it.")
|
||||||
(li (strong "AI is a native speaker.") " An AI reading SX reads the same notation as the server, the client, the wire, and the spec. There is no translation layer. The AI does not generate code that must be compiled and deployed — it generates expressions that are evaluated. The medium is shared between human, machine, and network.")))
|
(li (strong "AI is a native speaker.") " An AI reading SX reads the same notation as the server, the client, the wire, and the spec. There is no translation layer. The AI does not generate code that must be compiled and deployed — it generates expressions that are evaluated. The medium is shared between human, machine, and network.")))
|
||||||
|
|
||||||
(~doc-section :title "The metacircular web" :id "metacircular-web"
|
(~doc-section :title "The metacircular web" :id "metacircular-web"
|
||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
"McCarthy's " (a :href "https://en.wikipedia.org/wiki/Lisp_(programming_language)#Connection_to_artificial_intelligence" :class "text-violet-600 hover:underline" "metacircular evaluator") " proved that a computing language can define itself. SX extends this proof to a networked hypermedium:")
|
"McCarthy's " (a :href "https://en.wikipedia.org/wiki/Lisp_(programming_language)#Connection_to_artificial_intelligence" :class "text-violet-600 hover:underline" "metacircular evaluator") " proved that a computing language can define itself. SX extends this proof to a networked hypermedium:")
|
||||||
(ul :class "space-y-2 text-stone-600 mt-2"
|
(ul :class "space-y-2 text-stone-600 mt-2"
|
||||||
(li "The " (strong "evaluator") " is defined in SX: " (a :href "/(language.(spec.eval))" :class "text-violet-600 hover:underline" "eval.sx") " specifies the evaluation rules for all expression types, and this specification is itself an SX document.")
|
(li "The " (strong "evaluator") " is defined in SX: " (a :href "/sx/(language.(spec.eval))" :class "text-violet-600 hover:underline" "eval.sx") " specifies the evaluation rules for all expression types, and this specification is itself an SX document.")
|
||||||
(li "The " (strong "parser") " is defined in SX: " (a :href "/(language.(spec.parser))" :class "text-violet-600 hover:underline" "parser.sx") " specifies tokenization, grammar, and serialization — written in the language it parses.")
|
(li "The " (strong "parser") " is defined in SX: " (a :href "/sx/(language.(spec.parser))" :class "text-violet-600 hover:underline" "parser.sx") " specifies tokenization, grammar, and serialization — written in the language it parses.")
|
||||||
(li "The " (strong "renderer") " is defined in SX: " (a :href "/(language.(spec.render))" :class "text-violet-600 hover:underline" "render.sx") " specifies how s-expressions become HTML, SX wire format, or DOM nodes.")
|
(li "The " (strong "renderer") " is defined in SX: " (a :href "/sx/(language.(spec.render))" :class "text-violet-600 hover:underline" "render.sx") " specifies how s-expressions become HTML, SX wire format, or DOM nodes.")
|
||||||
(li "The " (strong "router") " is an expression: " (code "/(language.(doc.introduction))") " is a function call that resolves to content. " (a :href "/(language.(spec.router))" :class "text-violet-600 hover:underline" "router.sx") " specifies the matching — in SX.")
|
(li "The " (strong "router") " is an expression: " (code "/sx/(language.(doc.introduction))") " is a function call that resolves to content. " (a :href "/sx/(language.(spec.router))" :class "text-violet-600 hover:underline" "router.sx") " specifies the matching — in SX.")
|
||||||
(li "The " (strong "site itself") " is s-expressions: this essay, the navigation tree, the component registry, the wire protocol — all the way down."))
|
(li "The " (strong "site itself") " is s-expressions: this essay, the navigation tree, the component registry, the wire protocol — all the way down."))
|
||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
"At every level, the description and the described are the same kind of thing. The specification is not " (em "about") " the system — it " (em "is") " the system. This is not metaphor. It is the literal architecture."))
|
"At every level, the description and the described are the same kind of thing. The specification is not " (em "about") " the system — it " (em "is") " the system. This is not metaphor. It is the literal architecture."))
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
"This makes it trivial to build AI pipelines that generate SX. Parse the output. If it parses, evaluate it in a sandbox. If it does not parse, the error is always the same kind — unmatched parentheses — and the fix is always mechanical. There is no \"your JSX is invalid because you used " (code "class") " instead of " (code "className") "\" or \"you forgot the semicolon after the type annotation but before the generic constraint.\"")
|
"This makes it trivial to build AI pipelines that generate SX. Parse the output. If it parses, evaluate it in a sandbox. If it does not parse, the error is always the same kind — unmatched parentheses — and the fix is always mechanical. There is no \"your JSX is invalid because you used " (code "class") " instead of " (code "className") "\" or \"you forgot the semicolon after the type annotation but before the generic constraint.\"")
|
||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
"Beyond parsing, the SX " (a :href "/(language.(spec.primitives))" :class "text-violet-600 hover:underline" "boundary system") " provides semantic validation. A pure component cannot call IO primitives — not by convention, but by the evaluator refusing to resolve them. An AI generating a component can produce whatever expressions it wants; the sandbox ensures only permitted operations execute. Validation is not a separate step bolted onto the pipeline. It is the language."))
|
"Beyond parsing, the SX " (a :href "/sx/(language.(spec.primitives))" :class "text-violet-600 hover:underline" "boundary system") " provides semantic validation. A pure component cannot call IO primitives — not by convention, but by the evaluator refusing to resolve them. An AI generating a component can produce whatever expressions it wants; the sandbox ensures only permitted operations execute. Validation is not a separate step bolted onto the pipeline. It is the language."))
|
||||||
|
|
||||||
(~doc-section :title "Components are self-documenting" :id "self-documenting"
|
(~doc-section :title "Components are self-documenting" :id "self-documenting"
|
||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
@@ -98,7 +98,7 @@
|
|||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
"This is only possible because the representation is uniform. The AI does not need to switch between \"writing HTML mode\" and \"writing CSS mode\" and \"writing JavaScript mode\" and \"writing deployment config mode.\" It is always writing s-expressions. The cognitive load is constant. The error rate is constant. The speed is constant — regardless of whether it is generating a page layout, a macro expander, or a Docker healthcheck.")
|
"This is only possible because the representation is uniform. The AI does not need to switch between \"writing HTML mode\" and \"writing CSS mode\" and \"writing JavaScript mode\" and \"writing deployment config mode.\" It is always writing s-expressions. The cognitive load is constant. The error rate is constant. The speed is constant — regardless of whether it is generating a page layout, a macro expander, or a Docker healthcheck.")
|
||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
"The " (a :href "/(etc.(essay.sx-sucks))" :class "text-violet-600 hover:underline" "sx sucks") " essay copped to the AI authorship and framed it as a weakness — microwave dinner on a nice plate. But the framing was wrong. If a language is so well-suited to machine generation that one person with no Lisp experience can build a self-hosting language, a multi-target bootstrapper, a reactive component framework, and a full documentation site through pure agentic AI — that is not a weakness of the language. That is the language working exactly as it should."))
|
"The " (a :href "/sx/(etc.(essay.sx-sucks))" :class "text-violet-600 hover:underline" "sx sucks") " essay copped to the AI authorship and framed it as a weakness — microwave dinner on a nice plate. But the framing was wrong. If a language is so well-suited to machine generation that one person with no Lisp experience can build a self-hosting language, a multi-target bootstrapper, a reactive component framework, and a full documentation site through pure agentic AI — that is not a weakness of the language. That is the language working exactly as it should."))
|
||||||
|
|
||||||
(~doc-section :title "What this changes" :id "what-changes"
|
(~doc-section :title "What this changes" :id "what-changes"
|
||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -127,7 +127,7 @@
|
|||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
"Every page you are reading was produced through conversation with an agentic AI. The SX evaluator — a self-hosting interpreter with tail-call optimization, delimited continuations, macro expansion, and three rendering backends — was developed without opening a code editor. The specification files that define the language were written without an IDE. The bootstrappers that compile the spec to JavaScript and Python were produced without syntax highlighting or autocomplete. The test suite — hundreds of tests across evaluator, parser, renderer, router, dependency analyzer, and engine — was written without a test runner GUI. This documentation site — with its navigation, its code examples, its live demos — was built without a web development framework's CLI.")
|
"Every page you are reading was produced through conversation with an agentic AI. The SX evaluator — a self-hosting interpreter with tail-call optimization, delimited continuations, macro expansion, and three rendering backends — was developed without opening a code editor. The specification files that define the language were written without an IDE. The bootstrappers that compile the spec to JavaScript and Python were produced without syntax highlighting or autocomplete. The test suite — hundreds of tests across evaluator, parser, renderer, router, dependency analyzer, and engine — was written without a test runner GUI. This documentation site — with its navigation, its code examples, its live demos — was built without a web development framework's CLI.")
|
||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
"The developer sat in a terminal. They described what they wanted. The AI produced the code. When something was wrong, they described what was wrong. The AI fixed it. When something needed to change, they described the change. The AI made the change. Across thousands of files, tens of thousands of lines of code, and months of development. Even the jokes — the " (a :href "/(etc.(essay.sx-sucks))" :class "text-violet-600 hover:underline" "self-deprecating essay") " about everything wrong with SX, the deadpan tone of the documentation, the essay you are reading right now — all produced through conversation, not through typing.")
|
"The developer sat in a terminal. They described what they wanted. The AI produced the code. When something was wrong, they described what was wrong. The AI fixed it. When something needed to change, they described the change. The AI made the change. Across thousands of files, tens of thousands of lines of code, and months of development. Even the jokes — the " (a :href "/sx/(etc.(essay.sx-sucks))" :class "text-violet-600 hover:underline" "self-deprecating essay") " about everything wrong with SX, the deadpan tone of the documentation, the essay you are reading right now — all produced through conversation, not through typing.")
|
||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
"No build step. No bundler. No transpiler. No package manager. No CSS preprocessor. No dev server. No linter. No formatter. No type checker. No framework CLI. No code editor.")
|
"No build step. No bundler. No transpiler. No package manager. No CSS preprocessor. No dev server. No linter. No formatter. No type checker. No framework CLI. No code editor.")
|
||||||
(p :class "text-stone-600"
|
(p :class "text-stone-600"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
:description "The simplest sx interaction: click a button, fetch content from the server, swap it in."
|
:description "The simplest sx interaction: click a button, fetch content from the server, swap it in."
|
||||||
:demo-description "Click the button to load server-rendered content."
|
:demo-description "Click the button to load server-rendered content."
|
||||||
:demo (~click-to-load-demo)
|
:demo (~click-to-load-demo)
|
||||||
:sx-code "(button\n :sx-get \"/(geography.(hypermedia.(example.(api.click))))\"\n :sx-target \"#click-result\"\n :sx-swap \"innerHTML\"\n \"Load content\")"
|
:sx-code "(button\n :sx-get \"/sx/(geography.(hypermedia.(example.(api.click))))\"\n :sx-target \"#click-result\"\n :sx-swap \"innerHTML\"\n \"Load content\")"
|
||||||
:handler-code (handler-source "ex-click")
|
:handler-code (handler-source "ex-click")
|
||||||
:comp-placeholder-id "click-comp"
|
:comp-placeholder-id "click-comp"
|
||||||
:wire-placeholder-id "click-wire"
|
:wire-placeholder-id "click-wire"
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
:description "Forms with sx-post submit via AJAX and swap the response into a target."
|
:description "Forms with sx-post submit via AJAX and swap the response into a target."
|
||||||
:demo-description "Enter a name and submit."
|
:demo-description "Enter a name and submit."
|
||||||
:demo (~form-demo)
|
:demo (~form-demo)
|
||||||
:sx-code "(form\n :sx-post \"/(geography.(hypermedia.(example.(api.form))))\"\n :sx-target \"#form-result\"\n :sx-swap \"innerHTML\"\n (input :type \"text\" :name \"name\")\n (button :type \"submit\" \"Submit\"))"
|
:sx-code "(form\n :sx-post \"/sx/(geography.(hypermedia.(example.(api.form))))\"\n :sx-target \"#form-result\"\n :sx-swap \"innerHTML\"\n (input :type \"text\" :name \"name\")\n (button :type \"submit\" \"Submit\"))"
|
||||||
:handler-code (handler-source "ex-form")
|
:handler-code (handler-source "ex-form")
|
||||||
:comp-placeholder-id "form-comp"
|
:comp-placeholder-id "form-comp"
|
||||||
:wire-placeholder-id "form-wire"))
|
:wire-placeholder-id "form-wire"))
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
:description "Use sx-trigger with \"every\" to poll the server at regular intervals."
|
:description "Use sx-trigger with \"every\" to poll the server at regular intervals."
|
||||||
:demo-description "This div polls the server every 2 seconds."
|
:demo-description "This div polls the server every 2 seconds."
|
||||||
:demo (~polling-demo)
|
:demo (~polling-demo)
|
||||||
:sx-code "(div\n :sx-get \"/(geography.(hypermedia.(example.(api.poll))))\"\n :sx-trigger \"load, every 2s\"\n :sx-swap \"innerHTML\"\n \"Loading...\")"
|
:sx-code "(div\n :sx-get \"/sx/(geography.(hypermedia.(example.(api.poll))))\"\n :sx-trigger \"load, every 2s\"\n :sx-swap \"innerHTML\"\n \"Loading...\")"
|
||||||
:handler-code (handler-source "ex-poll")
|
:handler-code (handler-source "ex-poll")
|
||||||
:comp-placeholder-id "poll-comp"
|
:comp-placeholder-id "poll-comp"
|
||||||
:wire-placeholder-id "poll-wire"
|
:wire-placeholder-id "poll-wire"
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
(list "3" "Write documentation")
|
(list "3" "Write documentation")
|
||||||
(list "4" "Deploy to production")
|
(list "4" "Deploy to production")
|
||||||
(list "5" "Add unit tests")))
|
(list "5" "Add unit tests")))
|
||||||
:sx-code "(button\n :sx-delete \"/(geography.(hypermedia.(example.(api.(delete.1)))))\"\n :sx-target \"#row-1\"\n :sx-swap \"outerHTML\"\n :sx-confirm \"Delete this item?\"\n \"delete\")"
|
:sx-code "(button\n :sx-delete \"/sx/(geography.(hypermedia.(example.(api.(delete.1)))))\"\n :sx-target \"#row-1\"\n :sx-swap \"outerHTML\"\n :sx-confirm \"Delete this item?\"\n \"delete\")"
|
||||||
:handler-code (handler-source "ex-delete")
|
:handler-code (handler-source "ex-delete")
|
||||||
:comp-placeholder-id "delete-comp"
|
:comp-placeholder-id "delete-comp"
|
||||||
:wire-placeholder-id "delete-wire"
|
:wire-placeholder-id "delete-wire"
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
:description "sx-swap-oob lets a single response update multiple elements anywhere in the DOM."
|
:description "sx-swap-oob lets a single response update multiple elements anywhere in the DOM."
|
||||||
:demo-description "One request updates both Box A (via sx-target) and Box B (via sx-swap-oob)."
|
:demo-description "One request updates both Box A (via sx-target) and Box B (via sx-swap-oob)."
|
||||||
:demo (~oob-demo)
|
:demo (~oob-demo)
|
||||||
:sx-code ";; Button targets Box A\n(button\n :sx-get \"/(geography.(hypermedia.(example.(api.oob))))\"\n :sx-target \"#oob-box-a\"\n :sx-swap \"innerHTML\"\n \"Update both boxes\")"
|
:sx-code ";; Button targets Box A\n(button\n :sx-get \"/sx/(geography.(hypermedia.(example.(api.oob))))\"\n :sx-target \"#oob-box-a\"\n :sx-swap \"innerHTML\"\n \"Update both boxes\")"
|
||||||
:handler-code (handler-source "ex-oob")
|
:handler-code (handler-source "ex-oob")
|
||||||
:wire-placeholder-id "oob-wire"
|
:wire-placeholder-id "oob-wire"
|
||||||
:wire-note "The fragment contains both the main content and an OOB element. sx.js splits them: main content goes to sx-target, OOB elements find their targets by ID."))
|
:wire-note "The fragment contains both the main content and an OOB element. sx.js splits them: main content goes to sx-target, OOB elements find their targets by ID."))
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
:description "Use sx-trigger=\"load\" to fetch content as soon as the element enters the DOM. Great for deferring expensive content below the fold."
|
:description "Use sx-trigger=\"load\" to fetch content as soon as the element enters the DOM. Great for deferring expensive content below the fold."
|
||||||
:demo-description "Content loads automatically when the page renders."
|
:demo-description "Content loads automatically when the page renders."
|
||||||
:demo (~lazy-loading-demo)
|
:demo (~lazy-loading-demo)
|
||||||
:sx-code "(div\n :sx-get \"/(geography.(hypermedia.(example.(api.lazy))))\"\n :sx-trigger \"load\"\n :sx-swap \"innerHTML\"\n (div :class \"animate-pulse\" \"Loading...\"))"
|
:sx-code "(div\n :sx-get \"/sx/(geography.(hypermedia.(example.(api.lazy))))\"\n :sx-trigger \"load\"\n :sx-swap \"innerHTML\"\n (div :class \"animate-pulse\" \"Loading...\"))"
|
||||||
:handler-code (handler-source "ex-lazy")
|
:handler-code (handler-source "ex-lazy")
|
||||||
:comp-placeholder-id "lazy-comp"
|
:comp-placeholder-id "lazy-comp"
|
||||||
:wire-placeholder-id "lazy-wire"))
|
:wire-placeholder-id "lazy-wire"))
|
||||||
@@ -95,7 +95,7 @@
|
|||||||
:description "A sentinel element at the bottom uses sx-trigger=\"intersect once\" to load the next page when scrolled into view. Each response appends items and a new sentinel."
|
:description "A sentinel element at the bottom uses sx-trigger=\"intersect once\" to load the next page when scrolled into view. Each response appends items and a new sentinel."
|
||||||
:demo-description "Scroll down in the container to load more items (5 pages total)."
|
:demo-description "Scroll down in the container to load more items (5 pages total)."
|
||||||
:demo (~infinite-scroll-demo)
|
:demo (~infinite-scroll-demo)
|
||||||
:sx-code "(div :id \"scroll-sentinel\"\n :sx-get \"/(geography.(hypermedia.(example.(api.scroll))))?page=2\"\n :sx-trigger \"intersect once\"\n :sx-target \"#scroll-items\"\n :sx-swap \"beforeend\"\n \"Loading more...\")"
|
:sx-code "(div :id \"scroll-sentinel\"\n :sx-get \"/sx/(geography.(hypermedia.(example.(api.scroll))))?page=2\"\n :sx-trigger \"intersect once\"\n :sx-target \"#scroll-items\"\n :sx-swap \"beforeend\"\n \"Loading more...\")"
|
||||||
:handler-code (handler-source "ex-scroll")
|
:handler-code (handler-source "ex-scroll")
|
||||||
:comp-placeholder-id "scroll-comp"
|
:comp-placeholder-id "scroll-comp"
|
||||||
:wire-placeholder-id "scroll-wire"))
|
:wire-placeholder-id "scroll-wire"))
|
||||||
@@ -106,7 +106,7 @@
|
|||||||
:description "Start a server-side job, then poll for progress using sx-trigger=\"load delay:500ms\" on each response. The bar fills up and stops when complete."
|
:description "Start a server-side job, then poll for progress using sx-trigger=\"load delay:500ms\" on each response. The bar fills up and stops when complete."
|
||||||
:demo-description "Click start to begin a simulated job."
|
:demo-description "Click start to begin a simulated job."
|
||||||
:demo (~progress-bar-demo)
|
:demo (~progress-bar-demo)
|
||||||
:sx-code ";; Start the job\n(button\n :sx-post \"/(geography.(hypermedia.(example.(api.progress-start))))\"\n :sx-target \"#progress-target\"\n :sx-swap \"innerHTML\")\n\n;; Each response re-polls via sx-trigger=\"load\"\n(div :sx-get \"/(geography.(hypermedia.(example.(api.progress-status))))?job=ID\"\n :sx-trigger \"load delay:500ms\"\n :sx-target \"#progress-target\"\n :sx-swap \"innerHTML\")"
|
:sx-code ";; Start the job\n(button\n :sx-post \"/sx/(geography.(hypermedia.(example.(api.progress-start))))\"\n :sx-target \"#progress-target\"\n :sx-swap \"innerHTML\")\n\n;; Each response re-polls via sx-trigger=\"load\"\n(div :sx-get \"/sx/(geography.(hypermedia.(example.(api.progress-status))))?job=ID\"\n :sx-trigger \"load delay:500ms\"\n :sx-target \"#progress-target\"\n :sx-swap \"innerHTML\")"
|
||||||
:handler-code (str (handler-source "ex-progress-start") "\n\n" (handler-source "ex-progress-status"))
|
:handler-code (str (handler-source "ex-progress-start") "\n\n" (handler-source "ex-progress-status"))
|
||||||
:comp-placeholder-id "progress-comp"
|
:comp-placeholder-id "progress-comp"
|
||||||
:wire-placeholder-id "progress-wire"))
|
:wire-placeholder-id "progress-wire"))
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
:description "An input with sx-trigger=\"keyup delay:300ms changed\" debounces keystrokes and only fires when the value changes. The server filters a list of programming languages."
|
:description "An input with sx-trigger=\"keyup delay:300ms changed\" debounces keystrokes and only fires when the value changes. The server filters a list of programming languages."
|
||||||
:demo-description "Type to search through 20 programming languages."
|
:demo-description "Type to search through 20 programming languages."
|
||||||
:demo (~active-search-demo)
|
:demo (~active-search-demo)
|
||||||
:sx-code "(input :type \"text\" :name \"q\"\n :sx-get \"/(geography.(hypermedia.(example.(api.search))))\"\n :sx-trigger \"keyup delay:300ms changed\"\n :sx-target \"#search-results\"\n :sx-swap \"innerHTML\"\n :placeholder \"Search...\")"
|
:sx-code "(input :type \"text\" :name \"q\"\n :sx-get \"/sx/(geography.(hypermedia.(example.(api.search))))\"\n :sx-trigger \"keyup delay:300ms changed\"\n :sx-target \"#search-results\"\n :sx-swap \"innerHTML\"\n :placeholder \"Search...\")"
|
||||||
:handler-code (handler-source "ex-search")
|
:handler-code (handler-source "ex-search")
|
||||||
:comp-placeholder-id "search-comp"
|
:comp-placeholder-id "search-comp"
|
||||||
:wire-placeholder-id "search-wire"))
|
:wire-placeholder-id "search-wire"))
|
||||||
@@ -128,7 +128,7 @@
|
|||||||
:description "Validate an email field on blur. The server checks format and whether it is taken, returning green or red feedback inline."
|
:description "Validate an email field on blur. The server checks format and whether it is taken, returning green or red feedback inline."
|
||||||
:demo-description "Enter an email and click away (blur) to validate."
|
:demo-description "Enter an email and click away (blur) to validate."
|
||||||
:demo (~inline-validation-demo)
|
:demo (~inline-validation-demo)
|
||||||
:sx-code "(input :type \"text\" :name \"email\"\n :sx-get \"/(geography.(hypermedia.(example.(api.validate))))\"\n :sx-trigger \"blur\"\n :sx-target \"#email-feedback\"\n :sx-swap \"innerHTML\"\n :placeholder \"user@example.com\")"
|
:sx-code "(input :type \"text\" :name \"email\"\n :sx-get \"/sx/(geography.(hypermedia.(example.(api.validate))))\"\n :sx-trigger \"blur\"\n :sx-target \"#email-feedback\"\n :sx-swap \"innerHTML\"\n :placeholder \"user@example.com\")"
|
||||||
:handler-code (handler-source "ex-validate")
|
:handler-code (handler-source "ex-validate")
|
||||||
:comp-placeholder-id "validate-comp"
|
:comp-placeholder-id "validate-comp"
|
||||||
:wire-placeholder-id "validate-wire"))
|
:wire-placeholder-id "validate-wire"))
|
||||||
@@ -139,7 +139,7 @@
|
|||||||
:description "Two linked selects: pick a category and the second select updates with matching items via sx-get."
|
:description "Two linked selects: pick a category and the second select updates with matching items via sx-get."
|
||||||
:demo-description "Select a category to populate the item dropdown."
|
:demo-description "Select a category to populate the item dropdown."
|
||||||
:demo (~value-select-demo)
|
:demo (~value-select-demo)
|
||||||
:sx-code "(select :name \"category\"\n :sx-get \"/(geography.(hypermedia.(example.(api.values))))\"\n :sx-trigger \"change\"\n :sx-target \"#value-items\"\n :sx-swap \"innerHTML\"\n (option \"Languages\")\n (option \"Frameworks\")\n (option \"Databases\"))"
|
:sx-code "(select :name \"category\"\n :sx-get \"/sx/(geography.(hypermedia.(example.(api.values))))\"\n :sx-trigger \"change\"\n :sx-target \"#value-items\"\n :sx-swap \"innerHTML\"\n (option \"Languages\")\n (option \"Frameworks\")\n (option \"Databases\"))"
|
||||||
:handler-code (handler-source "ex-values")
|
:handler-code (handler-source "ex-values")
|
||||||
:comp-placeholder-id "values-comp"
|
:comp-placeholder-id "values-comp"
|
||||||
:wire-placeholder-id "values-wire"))
|
:wire-placeholder-id "values-wire"))
|
||||||
@@ -150,7 +150,7 @@
|
|||||||
:description "Use sx-on:afterSwap=\"this.reset()\" to clear form inputs after a successful submission."
|
:description "Use sx-on:afterSwap=\"this.reset()\" to clear form inputs after a successful submission."
|
||||||
:demo-description "Submit a message — the input resets after each send."
|
:demo-description "Submit a message — the input resets after each send."
|
||||||
:demo (~reset-on-submit-demo)
|
:demo (~reset-on-submit-demo)
|
||||||
:sx-code "(form :id \"reset-form\"\n :sx-post \"/(geography.(hypermedia.(example.(api.reset-submit))))\"\n :sx-target \"#reset-result\"\n :sx-swap \"innerHTML\"\n :sx-on:afterSwap \"this.reset()\"\n (input :type \"text\" :name \"message\")\n (button :type \"submit\" \"Send\"))"
|
:sx-code "(form :id \"reset-form\"\n :sx-post \"/sx/(geography.(hypermedia.(example.(api.reset-submit))))\"\n :sx-target \"#reset-result\"\n :sx-swap \"innerHTML\"\n :sx-on:afterSwap \"this.reset()\"\n (input :type \"text\" :name \"message\")\n (button :type \"submit\" \"Send\"))"
|
||||||
:handler-code (handler-source "ex-reset-submit")
|
:handler-code (handler-source "ex-reset-submit")
|
||||||
:comp-placeholder-id "reset-comp"
|
:comp-placeholder-id "reset-comp"
|
||||||
:wire-placeholder-id "reset-wire"))
|
:wire-placeholder-id "reset-wire"))
|
||||||
@@ -165,7 +165,7 @@
|
|||||||
(list "2" "Widget B" "24.50" "89")
|
(list "2" "Widget B" "24.50" "89")
|
||||||
(list "3" "Widget C" "12.00" "305")
|
(list "3" "Widget C" "12.00" "305")
|
||||||
(list "4" "Widget D" "45.00" "67")))
|
(list "4" "Widget D" "45.00" "67")))
|
||||||
:sx-code "(button\n :sx-get \"/(geography.(hypermedia.(example.(api.(editrow.1)))))\"\n :sx-target \"#erow-1\"\n :sx-swap \"outerHTML\"\n \"edit\")\n\n;; Save sends form data via POST\n(button\n :sx-post \"/(geography.(hypermedia.(example.(api.(editrow.1)))))\"\n :sx-target \"#erow-1\"\n :sx-swap \"outerHTML\"\n :sx-include \"#erow-1\"\n \"save\")"
|
:sx-code "(button\n :sx-get \"/sx/(geography.(hypermedia.(example.(api.(editrow.1)))))\"\n :sx-target \"#erow-1\"\n :sx-swap \"outerHTML\"\n \"edit\")\n\n;; Save sends form data via POST\n(button\n :sx-post \"/sx/(geography.(hypermedia.(example.(api.(editrow.1)))))\"\n :sx-target \"#erow-1\"\n :sx-swap \"outerHTML\"\n :sx-include \"#erow-1\"\n \"save\")"
|
||||||
:handler-code (str (handler-source "ex-editrow-form") "\n\n" (handler-source "ex-editrow-save"))
|
:handler-code (str (handler-source "ex-editrow-form") "\n\n" (handler-source "ex-editrow-save"))
|
||||||
:comp-placeholder-id "editrow-comp"
|
:comp-placeholder-id "editrow-comp"
|
||||||
:wire-placeholder-id "editrow-wire"))
|
:wire-placeholder-id "editrow-wire"))
|
||||||
@@ -181,7 +181,7 @@
|
|||||||
(list "3" "Carol Zhang" "carol@example.com" "active")
|
(list "3" "Carol Zhang" "carol@example.com" "active")
|
||||||
(list "4" "Dan Okafor" "dan@example.com" "inactive")
|
(list "4" "Dan Okafor" "dan@example.com" "inactive")
|
||||||
(list "5" "Eve Larsson" "eve@example.com" "active")))
|
(list "5" "Eve Larsson" "eve@example.com" "active")))
|
||||||
:sx-code "(button\n :sx-post \"/(geography.(hypermedia.(example.(api.bulk))))?action=activate\"\n :sx-target \"#bulk-table\"\n :sx-swap \"innerHTML\"\n :sx-include \"#bulk-form\"\n \"Activate\")"
|
:sx-code "(button\n :sx-post \"/sx/(geography.(hypermedia.(example.(api.bulk))))?action=activate\"\n :sx-target \"#bulk-table\"\n :sx-swap \"innerHTML\"\n :sx-include \"#bulk-form\"\n \"Activate\")"
|
||||||
:handler-code (handler-source "ex-bulk")
|
:handler-code (handler-source "ex-bulk")
|
||||||
:comp-placeholder-id "bulk-comp"
|
:comp-placeholder-id "bulk-comp"
|
||||||
:wire-placeholder-id "bulk-wire"))
|
:wire-placeholder-id "bulk-wire"))
|
||||||
@@ -192,7 +192,7 @@
|
|||||||
:description "Demonstrates different swap modes: beforeend appends, afterbegin prepends, and none skips the main swap while still processing OOB updates."
|
:description "Demonstrates different swap modes: beforeend appends, afterbegin prepends, and none skips the main swap while still processing OOB updates."
|
||||||
:demo-description "Try each button to see different swap behaviours."
|
:demo-description "Try each button to see different swap behaviours."
|
||||||
:demo (~swap-positions-demo)
|
:demo (~swap-positions-demo)
|
||||||
:sx-code ";; Append to end\n(button :sx-post \"/(geography.(hypermedia.(example.(api.swap-log))))?mode=beforeend\"\n :sx-target \"#swap-log\" :sx-swap \"beforeend\"\n \"Add to End\")\n\n;; Prepend to start\n(button :sx-post \"/(geography.(hypermedia.(example.(api.swap-log))))?mode=afterbegin\"\n :sx-target \"#swap-log\" :sx-swap \"afterbegin\"\n \"Add to Start\")\n\n;; No swap — OOB counter update only\n(button :sx-post \"/(geography.(hypermedia.(example.(api.swap-log))))?mode=none\"\n :sx-target \"#swap-log\" :sx-swap \"none\"\n \"Silent Ping\")"
|
:sx-code ";; Append to end\n(button :sx-post \"/sx/(geography.(hypermedia.(example.(api.swap-log))))?mode=beforeend\"\n :sx-target \"#swap-log\" :sx-swap \"beforeend\"\n \"Add to End\")\n\n;; Prepend to start\n(button :sx-post \"/sx/(geography.(hypermedia.(example.(api.swap-log))))?mode=afterbegin\"\n :sx-target \"#swap-log\" :sx-swap \"afterbegin\"\n \"Add to Start\")\n\n;; No swap — OOB counter update only\n(button :sx-post \"/sx/(geography.(hypermedia.(example.(api.swap-log))))?mode=none\"\n :sx-target \"#swap-log\" :sx-swap \"none\"\n \"Silent Ping\")"
|
||||||
:handler-code (handler-source "ex-swap-log")
|
:handler-code (handler-source "ex-swap-log")
|
||||||
:wire-placeholder-id "swap-wire"))
|
:wire-placeholder-id "swap-wire"))
|
||||||
|
|
||||||
@@ -202,7 +202,7 @@
|
|||||||
:description "sx-select lets the client pick a specific section from the server response by CSS selector. The server always returns the full dashboard — the client filters."
|
:description "sx-select lets the client pick a specific section from the server response by CSS selector. The server always returns the full dashboard — the client filters."
|
||||||
:demo-description "Different buttons select different parts of the same server response."
|
:demo-description "Different buttons select different parts of the same server response."
|
||||||
:demo (~select-filter-demo)
|
:demo (~select-filter-demo)
|
||||||
:sx-code ";; Pick just the stats section from the response\n(button\n :sx-get \"/(geography.(hypermedia.(example.(api.dashboard))))\"\n :sx-target \"#filter-target\"\n :sx-swap \"innerHTML\"\n :sx-select \"#dash-stats\"\n \"Stats Only\")\n\n;; No sx-select — get the full response\n(button\n :sx-get \"/(geography.(hypermedia.(example.(api.dashboard))))\"\n :sx-target \"#filter-target\"\n :sx-swap \"innerHTML\"\n \"Full Dashboard\")"
|
:sx-code ";; Pick just the stats section from the response\n(button\n :sx-get \"/sx/(geography.(hypermedia.(example.(api.dashboard))))\"\n :sx-target \"#filter-target\"\n :sx-swap \"innerHTML\"\n :sx-select \"#dash-stats\"\n \"Stats Only\")\n\n;; No sx-select — get the full response\n(button\n :sx-get \"/sx/(geography.(hypermedia.(example.(api.dashboard))))\"\n :sx-target \"#filter-target\"\n :sx-swap \"innerHTML\"\n \"Full Dashboard\")"
|
||||||
:handler-code (handler-source "ex-dashboard")
|
:handler-code (handler-source "ex-dashboard")
|
||||||
:wire-placeholder-id "filter-wire"))
|
:wire-placeholder-id "filter-wire"))
|
||||||
|
|
||||||
@@ -212,7 +212,7 @@
|
|||||||
:description "Tab navigation using sx-push-url to update the browser URL. Back/forward buttons navigate between previously visited tabs."
|
:description "Tab navigation using sx-push-url to update the browser URL. Back/forward buttons navigate between previously visited tabs."
|
||||||
:demo-description "Click tabs to switch content. Watch the browser URL change."
|
:demo-description "Click tabs to switch content. Watch the browser URL change."
|
||||||
:demo (~tabs-demo)
|
:demo (~tabs-demo)
|
||||||
:sx-code "(button\n :sx-get \"/(geography.(hypermedia.(example.(api.(tabs.tab1)))))\"\n :sx-target \"#tab-content\"\n :sx-swap \"innerHTML\"\n :sx-push-url \"/(geography.(hypermedia.(example.tabs)))?tab=tab1\"\n \"Overview\")"
|
:sx-code "(button\n :sx-get \"/sx/(geography.(hypermedia.(example.(api.(tabs.tab1)))))\"\n :sx-target \"#tab-content\"\n :sx-swap \"innerHTML\"\n :sx-push-url \"/sx/(geography.(hypermedia.(example.tabs)))?tab=tab1\"\n \"Overview\")"
|
||||||
:handler-code (handler-source "ex-tabs")
|
:handler-code (handler-source "ex-tabs")
|
||||||
:wire-placeholder-id "tabs-wire"))
|
:wire-placeholder-id "tabs-wire"))
|
||||||
|
|
||||||
@@ -222,7 +222,7 @@
|
|||||||
:description "CSS animations play on swap. The component injects a style tag with a keyframe animation and applies the class. Each click picks a random background colour."
|
:description "CSS animations play on swap. The component injects a style tag with a keyframe animation and applies the class. Each click picks a random background colour."
|
||||||
:demo-description "Click to swap in content with a fade-in animation."
|
:demo-description "Click to swap in content with a fade-in animation."
|
||||||
:demo (~animations-demo)
|
:demo (~animations-demo)
|
||||||
:sx-code "(button\n :sx-get \"/(geography.(hypermedia.(example.(api.animate))))\"\n :sx-target \"#anim-target\"\n :sx-swap \"innerHTML\"\n \"Load with animation\")\n\n;; Component uses CSS animation class\n(defcomp ~anim-result (&key color time)\n (div :class \"sx-fade-in ...\"\n (style \".sx-fade-in { animation: sxFadeIn 0.5s }\")\n (p \"Faded in!\")))"
|
:sx-code "(button\n :sx-get \"/sx/(geography.(hypermedia.(example.(api.animate))))\"\n :sx-target \"#anim-target\"\n :sx-swap \"innerHTML\"\n \"Load with animation\")\n\n;; Component uses CSS animation class\n(defcomp ~anim-result (&key color time)\n (div :class \"sx-fade-in ...\"\n (style \".sx-fade-in { animation: sxFadeIn 0.5s }\")\n (p \"Faded in!\")))"
|
||||||
:handler-code (handler-source "ex-animate")
|
:handler-code (handler-source "ex-animate")
|
||||||
:comp-placeholder-id "anim-comp"
|
:comp-placeholder-id "anim-comp"
|
||||||
:wire-placeholder-id "anim-wire"))
|
:wire-placeholder-id "anim-wire"))
|
||||||
@@ -233,7 +233,7 @@
|
|||||||
:description "Open a modal dialog by swapping in the dialog component. Close by swapping in empty content. Pure sx — no JavaScript library needed."
|
:description "Open a modal dialog by swapping in the dialog component. Close by swapping in empty content. Pure sx — no JavaScript library needed."
|
||||||
:demo-description "Click to open a modal dialog."
|
:demo-description "Click to open a modal dialog."
|
||||||
:demo (~dialogs-demo)
|
:demo (~dialogs-demo)
|
||||||
:sx-code "(button\n :sx-get \"/(geography.(hypermedia.(example.(api.dialog))))\"\n :sx-target \"#dialog-container\"\n :sx-swap \"innerHTML\"\n \"Open Dialog\")\n\n;; Dialog closes by swapping empty content\n(button\n :sx-get \"/(geography.(hypermedia.(example.(api.dialog-close))))\"\n :sx-target \"#dialog-container\"\n :sx-swap \"innerHTML\"\n \"Close\")"
|
:sx-code "(button\n :sx-get \"/sx/(geography.(hypermedia.(example.(api.dialog))))\"\n :sx-target \"#dialog-container\"\n :sx-swap \"innerHTML\"\n \"Open Dialog\")\n\n;; Dialog closes by swapping empty content\n(button\n :sx-get \"/sx/(geography.(hypermedia.(example.(api.dialog-close))))\"\n :sx-target \"#dialog-container\"\n :sx-swap \"innerHTML\"\n \"Close\")"
|
||||||
:handler-code (str (handler-source "ex-dialog") "\n\n" (handler-source "ex-dialog-close"))
|
:handler-code (str (handler-source "ex-dialog") "\n\n" (handler-source "ex-dialog-close"))
|
||||||
:comp-placeholder-id "dialog-comp"
|
:comp-placeholder-id "dialog-comp"
|
||||||
:wire-placeholder-id "dialog-wire"))
|
:wire-placeholder-id "dialog-wire"))
|
||||||
@@ -244,7 +244,7 @@
|
|||||||
:description "Use sx-trigger with keyup event filters and from:body to listen for global keyboard shortcuts. The filter prevents firing when typing in inputs."
|
:description "Use sx-trigger with keyup event filters and from:body to listen for global keyboard shortcuts. The filter prevents firing when typing in inputs."
|
||||||
:demo-description "Press s, n, or h on your keyboard."
|
:demo-description "Press s, n, or h on your keyboard."
|
||||||
:demo (~keyboard-shortcuts-demo)
|
:demo (~keyboard-shortcuts-demo)
|
||||||
:sx-code "(div :id \"kbd-target\"\n :sx-get \"/(geography.(hypermedia.(example.(api.keyboard))))?key=s\"\n :sx-trigger \"keyup[key=='s'&&!event.target.matches('input,textarea')] from:body\"\n :sx-swap \"innerHTML\"\n \"Press a shortcut key...\")"
|
:sx-code "(div :id \"kbd-target\"\n :sx-get \"/sx/(geography.(hypermedia.(example.(api.keyboard))))?key=s\"\n :sx-trigger \"keyup[key=='s'&&!event.target.matches('input,textarea')] from:body\"\n :sx-swap \"innerHTML\"\n \"Press a shortcut key...\")"
|
||||||
:handler-code (handler-source "ex-keyboard")
|
:handler-code (handler-source "ex-keyboard")
|
||||||
:comp-placeholder-id "kbd-comp"
|
:comp-placeholder-id "kbd-comp"
|
||||||
:wire-placeholder-id "kbd-wire"))
|
:wire-placeholder-id "kbd-wire"))
|
||||||
@@ -255,7 +255,7 @@
|
|||||||
:description "sx-put replaces the entire resource. This example shows a profile card with an Edit All button that sends a PUT with all fields."
|
:description "sx-put replaces the entire resource. This example shows a profile card with an Edit All button that sends a PUT with all fields."
|
||||||
:demo-description "Click Edit All to replace the full profile via PUT."
|
:demo-description "Click Edit All to replace the full profile via PUT."
|
||||||
:demo (~put-patch-demo :name "Ada Lovelace" :email "ada@example.com" :role "Engineer")
|
:demo (~put-patch-demo :name "Ada Lovelace" :email "ada@example.com" :role "Engineer")
|
||||||
:sx-code ";; Replace entire resource\n(form :sx-put \"/(geography.(hypermedia.(example.(api.putpatch))))\"\n :sx-target \"#pp-target\" :sx-swap \"innerHTML\"\n (input :name \"name\") (input :name \"email\")\n (button \"Save All (PUT)\"))"
|
:sx-code ";; Replace entire resource\n(form :sx-put \"/sx/(geography.(hypermedia.(example.(api.putpatch))))\"\n :sx-target \"#pp-target\" :sx-swap \"innerHTML\"\n (input :name \"name\") (input :name \"email\")\n (button \"Save All (PUT)\"))"
|
||||||
:handler-code (str (handler-source "ex-pp-edit-all") "\n\n" (handler-source "ex-pp-put"))
|
:handler-code (str (handler-source "ex-pp-edit-all") "\n\n" (handler-source "ex-pp-put"))
|
||||||
:comp-placeholder-id "pp-comp"
|
:comp-placeholder-id "pp-comp"
|
||||||
:wire-placeholder-id "pp-wire"))
|
:wire-placeholder-id "pp-wire"))
|
||||||
@@ -266,7 +266,7 @@
|
|||||||
:description "Use sx-encoding=\"json\" to send form data as a JSON body instead of URL-encoded form data. The server echoes back what it received."
|
:description "Use sx-encoding=\"json\" to send form data as a JSON body instead of URL-encoded form data. The server echoes back what it received."
|
||||||
:demo-description "Submit the form and see the JSON body the server received."
|
:demo-description "Submit the form and see the JSON body the server received."
|
||||||
:demo (~json-encoding-demo)
|
:demo (~json-encoding-demo)
|
||||||
:sx-code "(form\n :sx-post \"/(geography.(hypermedia.(example.(api.json-echo))))\"\n :sx-target \"#json-result\"\n :sx-swap \"innerHTML\"\n :sx-encoding \"json\"\n (input :name \"name\" :value \"Ada\")\n (input :type \"number\" :name \"age\" :value \"36\")\n (button \"Submit as JSON\"))"
|
:sx-code "(form\n :sx-post \"/sx/(geography.(hypermedia.(example.(api.json-echo))))\"\n :sx-target \"#json-result\"\n :sx-swap \"innerHTML\"\n :sx-encoding \"json\"\n (input :name \"name\" :value \"Ada\")\n (input :type \"number\" :name \"age\" :value \"36\")\n (button \"Submit as JSON\"))"
|
||||||
:handler-code (handler-source "ex-json-echo")
|
:handler-code (handler-source "ex-json-echo")
|
||||||
:comp-placeholder-id "json-comp"
|
:comp-placeholder-id "json-comp"
|
||||||
:wire-placeholder-id "json-wire"))
|
:wire-placeholder-id "json-wire"))
|
||||||
@@ -277,7 +277,7 @@
|
|||||||
:description "sx-vals adds extra key/value pairs to the request parameters. sx-headers adds custom HTTP headers. The server echoes back what it received."
|
:description "sx-vals adds extra key/value pairs to the request parameters. sx-headers adds custom HTTP headers. The server echoes back what it received."
|
||||||
:demo-description "Click each button to see what the server receives."
|
:demo-description "Click each button to see what the server receives."
|
||||||
:demo (~vals-headers-demo)
|
:demo (~vals-headers-demo)
|
||||||
:sx-code ";; Send extra values with the request\n(button\n :sx-get \"/(geography.(hypermedia.(example.(api.echo-vals))))\"\n :sx-vals \"{\\\"source\\\": \\\"button\\\"}\"\n \"Send with vals\")\n\n;; Send custom headers\n(button\n :sx-get \"/(geography.(hypermedia.(example.(api.echo-headers))))\"\n :sx-headers {:X-Custom-Token \"abc123\"}\n \"Send with headers\")"
|
:sx-code ";; Send extra values with the request\n(button\n :sx-get \"/sx/(geography.(hypermedia.(example.(api.echo-vals))))\"\n :sx-vals \"{\\\"source\\\": \\\"button\\\"}\"\n \"Send with vals\")\n\n;; Send custom headers\n(button\n :sx-get \"/sx/(geography.(hypermedia.(example.(api.echo-headers))))\"\n :sx-headers {:X-Custom-Token \"abc123\"}\n \"Send with headers\")"
|
||||||
:handler-code (str (handler-source "ex-echo-vals") "\n\n" (handler-source "ex-echo-headers"))
|
:handler-code (str (handler-source "ex-echo-vals") "\n\n" (handler-source "ex-echo-headers"))
|
||||||
:comp-placeholder-id "vals-comp"
|
:comp-placeholder-id "vals-comp"
|
||||||
:wire-placeholder-id "vals-wire"))
|
:wire-placeholder-id "vals-wire"))
|
||||||
@@ -288,7 +288,7 @@
|
|||||||
:description "sx.js adds the .sx-request CSS class to any element that has an active request. Use pure CSS to show spinners, disable buttons, or change opacity during loading."
|
:description "sx.js adds the .sx-request CSS class to any element that has an active request. Use pure CSS to show spinners, disable buttons, or change opacity during loading."
|
||||||
:demo-description "Click the button — it shows a spinner during the 2-second request."
|
:demo-description "Click the button — it shows a spinner during the 2-second request."
|
||||||
:demo (~loading-states-demo)
|
:demo (~loading-states-demo)
|
||||||
:sx-code ";; .sx-request class added during request\n(style \".sx-loading-btn.sx-request {\n opacity: 0.7; pointer-events: none; }\n.sx-loading-btn.sx-request .sx-spinner {\n display: inline-block; }\n.sx-loading-btn .sx-spinner {\n display: none; }\")\n\n(button :class \"sx-loading-btn\"\n :sx-get \"/(geography.(hypermedia.(example.(api.slow))))\"\n :sx-target \"#loading-result\"\n (span :class \"sx-spinner animate-spin\" \"...\")\n \"Load slow endpoint\")"
|
:sx-code ";; .sx-request class added during request\n(style \".sx-loading-btn.sx-request {\n opacity: 0.7; pointer-events: none; }\n.sx-loading-btn.sx-request .sx-spinner {\n display: inline-block; }\n.sx-loading-btn .sx-spinner {\n display: none; }\")\n\n(button :class \"sx-loading-btn\"\n :sx-get \"/sx/(geography.(hypermedia.(example.(api.slow))))\"\n :sx-target \"#loading-result\"\n (span :class \"sx-spinner animate-spin\" \"...\")\n \"Load slow endpoint\")"
|
||||||
:handler-code (handler-source "ex-slow")
|
:handler-code (handler-source "ex-slow")
|
||||||
:comp-placeholder-id "loading-comp"
|
:comp-placeholder-id "loading-comp"
|
||||||
:wire-placeholder-id "loading-wire"))
|
:wire-placeholder-id "loading-wire"))
|
||||||
@@ -299,7 +299,7 @@
|
|||||||
:description "sx-sync=\"replace\" aborts any in-flight request before sending a new one. This prevents stale responses from overwriting newer ones, even with random server delays."
|
:description "sx-sync=\"replace\" aborts any in-flight request before sending a new one. This prevents stale responses from overwriting newer ones, even with random server delays."
|
||||||
:demo-description "Type quickly — only the latest result appears despite random 0.5-2s server delays."
|
:demo-description "Type quickly — only the latest result appears despite random 0.5-2s server delays."
|
||||||
:demo (~sync-replace-demo)
|
:demo (~sync-replace-demo)
|
||||||
:sx-code "(input :type \"text\" :name \"q\"\n :sx-get \"/(geography.(hypermedia.(example.(api.slow-search))))\"\n :sx-trigger \"keyup delay:200ms changed\"\n :sx-target \"#sync-result\"\n :sx-swap \"innerHTML\"\n :sx-sync \"replace\"\n \"Type to search...\")"
|
:sx-code "(input :type \"text\" :name \"q\"\n :sx-get \"/sx/(geography.(hypermedia.(example.(api.slow-search))))\"\n :sx-trigger \"keyup delay:200ms changed\"\n :sx-target \"#sync-result\"\n :sx-swap \"innerHTML\"\n :sx-sync \"replace\"\n \"Type to search...\")"
|
||||||
:handler-code (handler-source "ex-slow-search")
|
:handler-code (handler-source "ex-slow-search")
|
||||||
:comp-placeholder-id "sync-comp"
|
:comp-placeholder-id "sync-comp"
|
||||||
:wire-placeholder-id "sync-wire"))
|
:wire-placeholder-id "sync-wire"))
|
||||||
@@ -310,7 +310,7 @@
|
|||||||
:description "sx-retry=\"exponential:1000:8000\" retries failed requests with exponential backoff starting at 1s up to 8s. The endpoint fails the first 2 attempts and succeeds on the 3rd."
|
:description "sx-retry=\"exponential:1000:8000\" retries failed requests with exponential backoff starting at 1s up to 8s. The endpoint fails the first 2 attempts and succeeds on the 3rd."
|
||||||
:demo-description "Click the button — watch it retry automatically after failures."
|
:demo-description "Click the button — watch it retry automatically after failures."
|
||||||
:demo (~retry-demo)
|
:demo (~retry-demo)
|
||||||
:sx-code "(button\n :sx-get \"/(geography.(hypermedia.(example.(api.flaky))))\"\n :sx-target \"#retry-result\"\n :sx-swap \"innerHTML\"\n :sx-retry \"exponential:1000:8000\"\n \"Call flaky endpoint\")"
|
:sx-code "(button\n :sx-get \"/sx/(geography.(hypermedia.(example.(api.flaky))))\"\n :sx-target \"#retry-result\"\n :sx-swap \"innerHTML\"\n :sx-retry \"exponential:1000:8000\"\n \"Call flaky endpoint\")"
|
||||||
:handler-code (handler-source "ex-flaky")
|
:handler-code (handler-source "ex-flaky")
|
||||||
:comp-placeholder-id "retry-comp"
|
:comp-placeholder-id "retry-comp"
|
||||||
:wire-placeholder-id "retry-wire"))
|
:wire-placeholder-id "retry-wire"))
|
||||||
|
|||||||
@@ -32,26 +32,26 @@
|
|||||||
(p :class "text-stone-600 mb-6"
|
(p :class "text-stone-600 mb-6"
|
||||||
"Complete reference for the sx client library.")
|
"Complete reference for the sx client library.")
|
||||||
(div :class "grid gap-4 sm:grid-cols-2"
|
(div :class "grid gap-4 sm:grid-cols-2"
|
||||||
(a :href "/(geography.(hypermedia.(reference.attributes)))"
|
(a :href "/sx/(geography.(hypermedia.(reference.attributes)))"
|
||||||
:sx-get "/(geography.(hypermedia.(reference.attributes)))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(geography.(hypermedia.(reference.attributes)))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
:class "block p-5 rounded-lg border border-stone-200 hover:border-violet-300 hover:shadow-sm transition-all no-underline"
|
:class "block p-5 rounded-lg border border-stone-200 hover:border-violet-300 hover:shadow-sm transition-all no-underline"
|
||||||
(h3 :class "text-lg font-semibold text-violet-700 mb-1" "Attributes")
|
(h3 :class "text-lg font-semibold text-violet-700 mb-1" "Attributes")
|
||||||
(p :class "text-stone-600 text-sm" "All sx attributes — request verbs, behavior modifiers, and sx-unique features."))
|
(p :class "text-stone-600 text-sm" "All sx attributes — request verbs, behavior modifiers, and sx-unique features."))
|
||||||
(a :href "/(geography.(hypermedia.(reference.headers)))"
|
(a :href "/sx/(geography.(hypermedia.(reference.headers)))"
|
||||||
:sx-get "/(geography.(hypermedia.(reference.headers)))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(geography.(hypermedia.(reference.headers)))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
:class "block p-5 rounded-lg border border-stone-200 hover:border-violet-300 hover:shadow-sm transition-all no-underline"
|
:class "block p-5 rounded-lg border border-stone-200 hover:border-violet-300 hover:shadow-sm transition-all no-underline"
|
||||||
(h3 :class "text-lg font-semibold text-violet-700 mb-1" "Headers")
|
(h3 :class "text-lg font-semibold text-violet-700 mb-1" "Headers")
|
||||||
(p :class "text-stone-600 text-sm" "Custom HTTP headers used to coordinate between the sx client and server."))
|
(p :class "text-stone-600 text-sm" "Custom HTTP headers used to coordinate between the sx client and server."))
|
||||||
(a :href "/(geography.(hypermedia.(reference.events)))"
|
(a :href "/sx/(geography.(hypermedia.(reference.events)))"
|
||||||
:sx-get "/(geography.(hypermedia.(reference.events)))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(geography.(hypermedia.(reference.events)))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
:class "block p-5 rounded-lg border border-stone-200 hover:border-violet-300 hover:shadow-sm transition-all no-underline"
|
:class "block p-5 rounded-lg border border-stone-200 hover:border-violet-300 hover:shadow-sm transition-all no-underline"
|
||||||
(h3 :class "text-lg font-semibold text-violet-700 mb-1" "Events")
|
(h3 :class "text-lg font-semibold text-violet-700 mb-1" "Events")
|
||||||
(p :class "text-stone-600 text-sm" "DOM events fired during the sx request lifecycle."))
|
(p :class "text-stone-600 text-sm" "DOM events fired during the sx request lifecycle."))
|
||||||
(a :href "/(geography.(hypermedia.(reference.js-api)))"
|
(a :href "/sx/(geography.(hypermedia.(reference.js-api)))"
|
||||||
:sx-get "/(geography.(hypermedia.(reference.js-api)))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(geography.(hypermedia.(reference.js-api)))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
:class "block p-5 rounded-lg border border-stone-200 hover:border-violet-300 hover:shadow-sm transition-all no-underline"
|
:class "block p-5 rounded-lg border border-stone-200 hover:border-violet-300 hover:shadow-sm transition-all no-underline"
|
||||||
(h3 :class "text-lg font-semibold text-violet-700 mb-1" "JS API")
|
(h3 :class "text-lg font-semibold text-violet-700 mb-1" "JS API")
|
||||||
|
|||||||
@@ -55,7 +55,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-click
|
(defhandler ex-click
|
||||||
:path "/(geography.(hypermedia.(example.(api.click))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.click))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-form
|
(defhandler ex-form
|
||||||
:path "/(geography.(hypermedia.(example.(api.form))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.form))))"
|
||||||
:method :post
|
:method :post
|
||||||
:csrf false
|
:csrf false
|
||||||
:returns "element"
|
:returns "element"
|
||||||
@@ -92,7 +92,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-poll
|
(defhandler ex-poll
|
||||||
:path "/(geography.(hypermedia.(example.(api.poll))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.poll))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-delete
|
(defhandler ex-delete
|
||||||
:path "/(geography.(hypermedia.(example.(api.(delete.<sx:item_id>)))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.(delete.<sx:item_id>)))))"
|
||||||
:method :delete
|
:method :delete
|
||||||
:csrf false
|
:csrf false
|
||||||
:returns "element"
|
:returns "element"
|
||||||
@@ -130,7 +130,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-edit-form
|
(defhandler ex-edit-form
|
||||||
:path "/(geography.(hypermedia.(example.(api.edit))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.edit))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -143,7 +143,7 @@
|
|||||||
:text (str "(~inline-edit-form :value \"" value "\")")))))
|
:text (str "(~inline-edit-form :value \"" value "\")")))))
|
||||||
|
|
||||||
(defhandler ex-edit-save
|
(defhandler ex-edit-save
|
||||||
:path "/(geography.(hypermedia.(example.(api.edit))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.edit))))"
|
||||||
:method :post
|
:method :post
|
||||||
:csrf false
|
:csrf false
|
||||||
:returns "element"
|
:returns "element"
|
||||||
@@ -157,7 +157,7 @@
|
|||||||
:text (str "(~inline-view :value \"" value "\")")))))
|
:text (str "(~inline-view :value \"" value "\")")))))
|
||||||
|
|
||||||
(defhandler ex-edit-cancel
|
(defhandler ex-edit-cancel
|
||||||
:path "/(geography.(hypermedia.(example.(api.edit-cancel))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.edit-cancel))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -175,7 +175,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-oob
|
(defhandler ex-oob
|
||||||
:path "/(geography.(hypermedia.(example.(api.oob))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.oob))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -195,7 +195,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-lazy
|
(defhandler ex-lazy
|
||||||
:path "/(geography.(hypermedia.(example.(api.lazy))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.lazy))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -213,7 +213,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-scroll
|
(defhandler ex-scroll
|
||||||
:path "/(geography.(hypermedia.(example.(api.scroll))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.scroll))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -227,7 +227,7 @@
|
|||||||
(range start (+ start 5)))
|
(range start (+ start 5)))
|
||||||
(if (<= (+ pg 1) 6)
|
(if (<= (+ pg 1) 6)
|
||||||
(div :id "scroll-sentinel"
|
(div :id "scroll-sentinel"
|
||||||
:sx-get (str "/(geography.(hypermedia.(example.(api.scroll))))?page=" (+ pg 1))
|
:sx-get (str "/sx/(geography.(hypermedia.(example.(api.scroll))))?page=" (+ pg 1))
|
||||||
:sx-trigger "intersect once"
|
:sx-trigger "intersect once"
|
||||||
:sx-target "#scroll-items"
|
:sx-target "#scroll-items"
|
||||||
:sx-swap "beforeend"
|
:sx-swap "beforeend"
|
||||||
@@ -244,7 +244,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-progress-start
|
(defhandler ex-progress-start
|
||||||
:path "/(geography.(hypermedia.(example.(api.progress-start))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.progress-start))))"
|
||||||
:method :post
|
:method :post
|
||||||
:csrf false
|
:csrf false
|
||||||
:returns "element"
|
:returns "element"
|
||||||
@@ -261,7 +261,7 @@
|
|||||||
:text (str "(~progress-status :percent 0 :job-id \"" job-id "\")"))))))
|
:text (str "(~progress-status :percent 0 :job-id \"" job-id "\")"))))))
|
||||||
|
|
||||||
(defhandler ex-progress-status
|
(defhandler ex-progress-status
|
||||||
:path "/(geography.(hypermedia.(example.(api.progress-status))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.progress-status))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -282,7 +282,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-search
|
(defhandler ex-search
|
||||||
:path "/(geography.(hypermedia.(example.(api.search))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.search))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -304,7 +304,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-validate
|
(defhandler ex-validate
|
||||||
:path "/(geography.(hypermedia.(example.(api.validate))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.validate))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -331,7 +331,7 @@
|
|||||||
:text (nth result 1))))))
|
:text (nth result 1))))))
|
||||||
|
|
||||||
(defhandler ex-validate-submit
|
(defhandler ex-validate-submit
|
||||||
:path "/(geography.(hypermedia.(example.(api.validate-submit))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.validate-submit))))"
|
||||||
:method :post
|
:method :post
|
||||||
:csrf false
|
:csrf false
|
||||||
:returns "element"
|
:returns "element"
|
||||||
@@ -347,7 +347,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-values
|
(defhandler ex-values
|
||||||
:path "/(geography.(hypermedia.(example.(api.values))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.values))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -367,7 +367,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-reset-submit
|
(defhandler ex-reset-submit
|
||||||
:path "/(geography.(hypermedia.(example.(api.reset-submit))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.reset-submit))))"
|
||||||
:method :post
|
:method :post
|
||||||
:csrf false
|
:csrf false
|
||||||
:returns "element"
|
:returns "element"
|
||||||
@@ -387,7 +387,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-editrow-form
|
(defhandler ex-editrow-form
|
||||||
:path "/(geography.(hypermedia.(example.(api.(editrow.<sx:row_id>)))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.(editrow.<sx:row_id>)))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key row-id)
|
(&key row-id)
|
||||||
@@ -402,7 +402,7 @@
|
|||||||
:text (str "(~edit-row-form :id \"" (get row "id") "\" ...)"))))))
|
:text (str "(~edit-row-form :id \"" (get row "id") "\" ...)"))))))
|
||||||
|
|
||||||
(defhandler ex-editrow-save
|
(defhandler ex-editrow-save
|
||||||
:path "/(geography.(hypermedia.(example.(api.(editrow.<sx:row_id>)))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.(editrow.<sx:row_id>)))))"
|
||||||
:method :post
|
:method :post
|
||||||
:csrf false
|
:csrf false
|
||||||
:returns "element"
|
:returns "element"
|
||||||
@@ -420,7 +420,7 @@
|
|||||||
:text (str "(~edit-row-view :id \"" row-id "\" ...)")))))
|
:text (str "(~edit-row-view :id \"" row-id "\" ...)")))))
|
||||||
|
|
||||||
(defhandler ex-editrow-cancel
|
(defhandler ex-editrow-cancel
|
||||||
:path "/(geography.(hypermedia.(example.(api.(editrow-cancel.<sx:row_id>)))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.(editrow-cancel.<sx:row_id>)))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key row-id)
|
(&key row-id)
|
||||||
@@ -439,7 +439,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-bulk
|
(defhandler ex-bulk
|
||||||
:path "/(geography.(hypermedia.(example.(api.bulk))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.bulk))))"
|
||||||
:method :post
|
:method :post
|
||||||
:csrf false
|
:csrf false
|
||||||
:returns "element"
|
:returns "element"
|
||||||
@@ -476,7 +476,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-swap-log
|
(defhandler ex-swap-log
|
||||||
:path "/(geography.(hypermedia.(example.(api.swap-log))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.swap-log))))"
|
||||||
:method :post
|
:method :post
|
||||||
:csrf false
|
:csrf false
|
||||||
:returns "element"
|
:returns "element"
|
||||||
@@ -500,7 +500,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-dashboard
|
(defhandler ex-dashboard
|
||||||
:path "/(geography.(hypermedia.(example.(api.dashboard))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.dashboard))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -530,7 +530,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-tabs
|
(defhandler ex-tabs
|
||||||
:path "/(geography.(hypermedia.(example.(api.(tabs.<sx:tab>)))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.(tabs.<sx:tab>)))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key tab)
|
(&key tab)
|
||||||
@@ -551,7 +551,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-animate
|
(defhandler ex-animate
|
||||||
:path "/(geography.(hypermedia.(example.(api.animate))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.animate))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -571,7 +571,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-dialog
|
(defhandler ex-dialog
|
||||||
:path "/(geography.(hypermedia.(example.(api.dialog))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.dialog))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -584,7 +584,7 @@
|
|||||||
:text "(~dialog-modal :title \"Confirm Action\" :message \"...\")")))
|
:text "(~dialog-modal :title \"Confirm Action\" :message \"...\")")))
|
||||||
|
|
||||||
(defhandler ex-dialog-close
|
(defhandler ex-dialog-close
|
||||||
:path "/(geography.(hypermedia.(example.(api.dialog-close))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.dialog-close))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -598,7 +598,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-keyboard
|
(defhandler ex-keyboard
|
||||||
:path "/(geography.(hypermedia.(example.(api.keyboard))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.keyboard))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -617,7 +617,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-pp-edit-all
|
(defhandler ex-pp-edit-all
|
||||||
:path "/(geography.(hypermedia.(example.(api.putpatch-edit-all))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.putpatch-edit-all))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -631,7 +631,7 @@
|
|||||||
:text (str "(~pp-form-full :name \"" (get p "name") "\" ...)")))))
|
:text (str "(~pp-form-full :name \"" (get p "name") "\" ...)")))))
|
||||||
|
|
||||||
(defhandler ex-pp-put
|
(defhandler ex-pp-put
|
||||||
:path "/(geography.(hypermedia.(example.(api.putpatch))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.putpatch))))"
|
||||||
:method :put
|
:method :put
|
||||||
:csrf false
|
:csrf false
|
||||||
:returns "element"
|
:returns "element"
|
||||||
@@ -648,7 +648,7 @@
|
|||||||
:text (str "(~pp-view :name \"" name "\" ...)")))))
|
:text (str "(~pp-view :name \"" name "\" ...)")))))
|
||||||
|
|
||||||
(defhandler ex-pp-cancel
|
(defhandler ex-pp-cancel
|
||||||
:path "/(geography.(hypermedia.(example.(api.putpatch-cancel))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.putpatch-cancel))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -667,7 +667,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-json-echo
|
(defhandler ex-json-echo
|
||||||
:path "/(geography.(hypermedia.(example.(api.json-echo))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.json-echo))))"
|
||||||
:method :post
|
:method :post
|
||||||
:csrf false
|
:csrf false
|
||||||
:returns "element"
|
:returns "element"
|
||||||
@@ -688,7 +688,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-echo-vals
|
(defhandler ex-echo-vals
|
||||||
:path "/(geography.(hypermedia.(example.(api.echo-vals))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.echo-vals))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -705,7 +705,7 @@
|
|||||||
:text (str "(~echo-result :label \"values\" :items (list ...))")))))))
|
:text (str "(~echo-result :label \"values\" :items (list ...))")))))))
|
||||||
|
|
||||||
(defhandler ex-echo-headers
|
(defhandler ex-echo-headers
|
||||||
:path "/(geography.(hypermedia.(example.(api.echo-headers))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.echo-headers))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -725,7 +725,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-slow
|
(defhandler ex-slow
|
||||||
:path "/(geography.(hypermedia.(example.(api.slow))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.slow))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -744,7 +744,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-slow-search
|
(defhandler ex-slow-search
|
||||||
:path "/(geography.(hypermedia.(example.(api.slow-search))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.slow-search))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -764,7 +764,7 @@
|
|||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|
||||||
(defhandler ex-flaky
|
(defhandler ex-flaky
|
||||||
:path "/(geography.(hypermedia.(example.(api.flaky))))"
|
:path "/sx/(geography.(hypermedia.(example.(api.flaky))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
;; --- sx-get demo: server time ---
|
;; --- sx-get demo: server time ---
|
||||||
|
|
||||||
(defhandler ref-time
|
(defhandler ref-time
|
||||||
:path "/(geography.(hypermedia.(reference.(api.time))))"
|
:path "/sx/(geography.(hypermedia.(reference.(api.time))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
;; --- sx-post demo: greet ---
|
;; --- sx-post demo: greet ---
|
||||||
|
|
||||||
(defhandler ref-greet
|
(defhandler ref-greet
|
||||||
:path "/(geography.(hypermedia.(reference.(api.greet))))"
|
:path "/sx/(geography.(hypermedia.(reference.(api.greet))))"
|
||||||
:method :post
|
:method :post
|
||||||
:csrf false
|
:csrf false
|
||||||
:returns "element"
|
:returns "element"
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
;; --- sx-put demo: status update ---
|
;; --- sx-put demo: status update ---
|
||||||
|
|
||||||
(defhandler ref-status
|
(defhandler ref-status
|
||||||
:path "/(geography.(hypermedia.(reference.(api.status))))"
|
:path "/sx/(geography.(hypermedia.(reference.(api.status))))"
|
||||||
:method :put
|
:method :put
|
||||||
:csrf false
|
:csrf false
|
||||||
:returns "element"
|
:returns "element"
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
;; --- sx-patch demo: theme ---
|
;; --- sx-patch demo: theme ---
|
||||||
|
|
||||||
(defhandler ref-theme
|
(defhandler ref-theme
|
||||||
:path "/(geography.(hypermedia.(reference.(api.theme))))"
|
:path "/sx/(geography.(hypermedia.(reference.(api.theme))))"
|
||||||
:method :patch
|
:method :patch
|
||||||
:csrf false
|
:csrf false
|
||||||
:returns "element"
|
:returns "element"
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
;; --- sx-delete demo ---
|
;; --- sx-delete demo ---
|
||||||
|
|
||||||
(defhandler ref-delete-item
|
(defhandler ref-delete-item
|
||||||
:path "/(geography.(hypermedia.(reference.(api.(item.<sx:item_id>)))))"
|
:path "/sx/(geography.(hypermedia.(reference.(api.(item.<sx:item_id>)))))"
|
||||||
:method :delete
|
:method :delete
|
||||||
:csrf false
|
:csrf false
|
||||||
:returns "element"
|
:returns "element"
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
;; --- sx-trigger demo: search ---
|
;; --- sx-trigger demo: search ---
|
||||||
|
|
||||||
(defhandler ref-trigger-search
|
(defhandler ref-trigger-search
|
||||||
:path "/(geography.(hypermedia.(reference.(api.trigger-search))))"
|
:path "/sx/(geography.(hypermedia.(reference.(api.trigger-search))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -89,7 +89,7 @@
|
|||||||
;; --- sx-swap demo ---
|
;; --- sx-swap demo ---
|
||||||
|
|
||||||
(defhandler ref-swap-item
|
(defhandler ref-swap-item
|
||||||
:path "/(geography.(hypermedia.(reference.(api.swap-item))))"
|
:path "/sx/(geography.(hypermedia.(reference.(api.swap-item))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -102,7 +102,7 @@
|
|||||||
;; --- sx-swap-oob demo ---
|
;; --- sx-swap-oob demo ---
|
||||||
|
|
||||||
(defhandler ref-oob
|
(defhandler ref-oob
|
||||||
:path "/(geography.(hypermedia.(reference.(api.oob))))"
|
:path "/sx/(geography.(hypermedia.(reference.(api.oob))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
;; --- sx-select demo ---
|
;; --- sx-select demo ---
|
||||||
|
|
||||||
(defhandler ref-select-page
|
(defhandler ref-select-page
|
||||||
:path "/(geography.(hypermedia.(reference.(api.select-page))))"
|
:path "/sx/(geography.(hypermedia.(reference.(api.select-page))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -134,7 +134,7 @@
|
|||||||
;; --- sx-sync demo: slow echo ---
|
;; --- sx-sync demo: slow echo ---
|
||||||
|
|
||||||
(defhandler ref-slow-echo
|
(defhandler ref-slow-echo
|
||||||
:path "/(geography.(hypermedia.(reference.(api.slow-echo))))"
|
:path "/sx/(geography.(hypermedia.(reference.(api.slow-echo))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -148,7 +148,7 @@
|
|||||||
;; --- sx-prompt demo ---
|
;; --- sx-prompt demo ---
|
||||||
|
|
||||||
(defhandler ref-prompt-echo
|
(defhandler ref-prompt-echo
|
||||||
:path "/(geography.(hypermedia.(reference.(api.prompt-echo))))"
|
:path "/sx/(geography.(hypermedia.(reference.(api.prompt-echo))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -161,7 +161,7 @@
|
|||||||
;; --- Error demo ---
|
;; --- Error demo ---
|
||||||
|
|
||||||
(defhandler ref-error-500
|
(defhandler ref-error-500
|
||||||
:path "/(geography.(hypermedia.(reference.(api.error-500))))"
|
:path "/sx/(geography.(hypermedia.(reference.(api.error-500))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "nil"
|
:returns "nil"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -175,7 +175,7 @@
|
|||||||
;; --- sx-encoding demo: file upload name ---
|
;; --- sx-encoding demo: file upload name ---
|
||||||
|
|
||||||
(defhandler ref-upload-name
|
(defhandler ref-upload-name
|
||||||
:path "/(geography.(hypermedia.(reference.(api.upload-name))))"
|
:path "/sx/(geography.(hypermedia.(reference.(api.upload-name))))"
|
||||||
:method :post
|
:method :post
|
||||||
:csrf false
|
:csrf false
|
||||||
:returns "element"
|
:returns "element"
|
||||||
@@ -190,7 +190,7 @@
|
|||||||
;; --- sx-headers demo: echo custom headers ---
|
;; --- sx-headers demo: echo custom headers ---
|
||||||
|
|
||||||
(defhandler ref-echo-headers
|
(defhandler ref-echo-headers
|
||||||
:path "/(geography.(hypermedia.(reference.(api.echo-headers))))"
|
:path "/sx/(geography.(hypermedia.(reference.(api.echo-headers))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -214,7 +214,7 @@
|
|||||||
;; --- sx-include demo: echo GET query params ---
|
;; --- sx-include demo: echo GET query params ---
|
||||||
|
|
||||||
(defhandler ref-echo-vals-get
|
(defhandler ref-echo-vals-get
|
||||||
:path "/(geography.(hypermedia.(reference.(api.echo-vals))))"
|
:path "/sx/(geography.(hypermedia.(reference.(api.echo-vals))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -235,7 +235,7 @@
|
|||||||
;; --- sx-vals demo: echo POST form values ---
|
;; --- sx-vals demo: echo POST form values ---
|
||||||
|
|
||||||
(defhandler ref-echo-vals-post
|
(defhandler ref-echo-vals-post
|
||||||
:path "/(geography.(hypermedia.(reference.(api.echo-vals))))"
|
:path "/sx/(geography.(hypermedia.(reference.(api.echo-vals))))"
|
||||||
:method :post
|
:method :post
|
||||||
:csrf false
|
:csrf false
|
||||||
:returns "element"
|
:returns "element"
|
||||||
@@ -257,7 +257,7 @@
|
|||||||
;; --- sx-retry demo: flaky endpoint (fails 2/3 times) ---
|
;; --- sx-retry demo: flaky endpoint (fails 2/3 times) ---
|
||||||
|
|
||||||
(defhandler ref-flaky
|
(defhandler ref-flaky
|
||||||
:path "/(geography.(hypermedia.(reference.(api.flaky))))"
|
:path "/sx/(geography.(hypermedia.(reference.(api.flaky))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -275,7 +275,7 @@
|
|||||||
;; --- sx-trigger-event demo: response header triggers ---
|
;; --- sx-trigger-event demo: response header triggers ---
|
||||||
|
|
||||||
(defhandler ref-trigger-event
|
(defhandler ref-trigger-event
|
||||||
:path "/(geography.(hypermedia.(reference.(api.trigger-event))))"
|
:path "/sx/(geography.(hypermedia.(reference.(api.trigger-event))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
@@ -287,7 +287,7 @@
|
|||||||
;; --- sx-retarget demo: response header retargets ---
|
;; --- sx-retarget demo: response header retargets ---
|
||||||
|
|
||||||
(defhandler ref-retarget
|
(defhandler ref-retarget
|
||||||
:path "/(geography.(hypermedia.(reference.(api.retarget))))"
|
:path "/sx/(geography.(hypermedia.(reference.(api.retarget))))"
|
||||||
:method :get
|
:method :get
|
||||||
:returns "element"
|
:returns "element"
|
||||||
(&key)
|
(&key)
|
||||||
|
|||||||
@@ -24,8 +24,8 @@
|
|||||||
(div :style (str (display "block") (max-w (get cssx-max-widths "3xl"))
|
(div :style (str (display "block") (max-w (get cssx-max-widths "3xl"))
|
||||||
(mx-auto) (px 4) (pt 8) (pb 4) (align "center"))
|
(mx-auto) (px 4) (pt 8) (pb 4) (align "center"))
|
||||||
;; Logo — only this navigates home
|
;; Logo — only this navigates home
|
||||||
(a :href "/"
|
(a :href "/sx/"
|
||||||
:sx-get "/" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
:style (str (display "block") (decoration "none"))
|
:style (str (display "block") (decoration "none"))
|
||||||
(lake :id "logo"
|
(lake :id "logo"
|
||||||
|
|||||||
@@ -3,246 +3,246 @@
|
|||||||
;; @css aria-selected:bg-violet-200 aria-selected:text-violet-900
|
;; @css aria-selected:bg-violet-200 aria-selected:text-violet-900
|
||||||
|
|
||||||
(define docs-nav-items (list
|
(define docs-nav-items (list
|
||||||
(dict :label "Introduction" :href "/(language.(doc.introduction))")
|
(dict :label "Introduction" :href "/sx/(language.(doc.introduction))")
|
||||||
(dict :label "Getting Started" :href "/(language.(doc.getting-started))")
|
(dict :label "Getting Started" :href "/sx/(language.(doc.getting-started))")
|
||||||
(dict :label "Components" :href "/(language.(doc.components))")
|
(dict :label "Components" :href "/sx/(language.(doc.components))")
|
||||||
(dict :label "Evaluator" :href "/(language.(doc.evaluator))")
|
(dict :label "Evaluator" :href "/sx/(language.(doc.evaluator))")
|
||||||
(dict :label "Primitives" :href "/(language.(doc.primitives))")
|
(dict :label "Primitives" :href "/sx/(language.(doc.primitives))")
|
||||||
(dict :label "Special Forms" :href "/(language.(doc.special-forms))")
|
(dict :label "Special Forms" :href "/sx/(language.(doc.special-forms))")
|
||||||
(dict :label "Server Rendering" :href "/(language.(doc.server-rendering))")))
|
(dict :label "Server Rendering" :href "/sx/(language.(doc.server-rendering))")))
|
||||||
|
|
||||||
(define reference-nav-items (list
|
(define reference-nav-items (list
|
||||||
(dict :label "Attributes" :href "/(geography.(hypermedia.(reference.attributes)))")
|
(dict :label "Attributes" :href "/sx/(geography.(hypermedia.(reference.attributes)))")
|
||||||
(dict :label "Headers" :href "/(geography.(hypermedia.(reference.headers)))")
|
(dict :label "Headers" :href "/sx/(geography.(hypermedia.(reference.headers)))")
|
||||||
(dict :label "Events" :href "/(geography.(hypermedia.(reference.events)))")
|
(dict :label "Events" :href "/sx/(geography.(hypermedia.(reference.events)))")
|
||||||
(dict :label "JS API" :href "/(geography.(hypermedia.(reference.js-api)))")))
|
(dict :label "JS API" :href "/sx/(geography.(hypermedia.(reference.js-api)))")))
|
||||||
|
|
||||||
(define protocols-nav-items (list
|
(define protocols-nav-items (list
|
||||||
(dict :label "Wire Format" :href "/(applications.(protocol.wire-format))")
|
(dict :label "Wire Format" :href "/sx/(applications.(protocol.wire-format))")
|
||||||
(dict :label "Fragments" :href "/(applications.(protocol.fragments))")
|
(dict :label "Fragments" :href "/sx/(applications.(protocol.fragments))")
|
||||||
(dict :label "Resolver I/O" :href "/(applications.(protocol.resolver-io))")
|
(dict :label "Resolver I/O" :href "/sx/(applications.(protocol.resolver-io))")
|
||||||
(dict :label "Internal Services" :href "/(applications.(protocol.internal-services))")
|
(dict :label "Internal Services" :href "/sx/(applications.(protocol.internal-services))")
|
||||||
(dict :label "ActivityPub" :href "/(applications.(protocol.activitypub))")
|
(dict :label "ActivityPub" :href "/sx/(applications.(protocol.activitypub))")
|
||||||
(dict :label "Future" :href "/(applications.(protocol.future))")))
|
(dict :label "Future" :href "/sx/(applications.(protocol.future))")))
|
||||||
|
|
||||||
(define examples-nav-items (list
|
(define examples-nav-items (list
|
||||||
(dict :label "Click to Load" :href "/(geography.(hypermedia.(example.click-to-load)))")
|
(dict :label "Click to Load" :href "/sx/(geography.(hypermedia.(example.click-to-load)))")
|
||||||
(dict :label "Form Submission" :href "/(geography.(hypermedia.(example.form-submission)))")
|
(dict :label "Form Submission" :href "/sx/(geography.(hypermedia.(example.form-submission)))")
|
||||||
(dict :label "Polling" :href "/(geography.(hypermedia.(example.polling)))")
|
(dict :label "Polling" :href "/sx/(geography.(hypermedia.(example.polling)))")
|
||||||
(dict :label "Delete Row" :href "/(geography.(hypermedia.(example.delete-row)))")
|
(dict :label "Delete Row" :href "/sx/(geography.(hypermedia.(example.delete-row)))")
|
||||||
(dict :label "Inline Edit" :href "/(geography.(hypermedia.(example.inline-edit)))")
|
(dict :label "Inline Edit" :href "/sx/(geography.(hypermedia.(example.inline-edit)))")
|
||||||
(dict :label "OOB Swaps" :href "/(geography.(hypermedia.(example.oob-swaps)))")
|
(dict :label "OOB Swaps" :href "/sx/(geography.(hypermedia.(example.oob-swaps)))")
|
||||||
(dict :label "Lazy Loading" :href "/(geography.(hypermedia.(example.lazy-loading)))")
|
(dict :label "Lazy Loading" :href "/sx/(geography.(hypermedia.(example.lazy-loading)))")
|
||||||
(dict :label "Infinite Scroll" :href "/(geography.(hypermedia.(example.infinite-scroll)))")
|
(dict :label "Infinite Scroll" :href "/sx/(geography.(hypermedia.(example.infinite-scroll)))")
|
||||||
(dict :label "Progress Bar" :href "/(geography.(hypermedia.(example.progress-bar)))")
|
(dict :label "Progress Bar" :href "/sx/(geography.(hypermedia.(example.progress-bar)))")
|
||||||
(dict :label "Active Search" :href "/(geography.(hypermedia.(example.active-search)))")
|
(dict :label "Active Search" :href "/sx/(geography.(hypermedia.(example.active-search)))")
|
||||||
(dict :label "Inline Validation" :href "/(geography.(hypermedia.(example.inline-validation)))")
|
(dict :label "Inline Validation" :href "/sx/(geography.(hypermedia.(example.inline-validation)))")
|
||||||
(dict :label "Value Select" :href "/(geography.(hypermedia.(example.value-select)))")
|
(dict :label "Value Select" :href "/sx/(geography.(hypermedia.(example.value-select)))")
|
||||||
(dict :label "Reset on Submit" :href "/(geography.(hypermedia.(example.reset-on-submit)))")
|
(dict :label "Reset on Submit" :href "/sx/(geography.(hypermedia.(example.reset-on-submit)))")
|
||||||
(dict :label "Edit Row" :href "/(geography.(hypermedia.(example.edit-row)))")
|
(dict :label "Edit Row" :href "/sx/(geography.(hypermedia.(example.edit-row)))")
|
||||||
(dict :label "Bulk Update" :href "/(geography.(hypermedia.(example.bulk-update)))")
|
(dict :label "Bulk Update" :href "/sx/(geography.(hypermedia.(example.bulk-update)))")
|
||||||
(dict :label "Swap Positions" :href "/(geography.(hypermedia.(example.swap-positions)))")
|
(dict :label "Swap Positions" :href "/sx/(geography.(hypermedia.(example.swap-positions)))")
|
||||||
(dict :label "Select Filter" :href "/(geography.(hypermedia.(example.select-filter)))")
|
(dict :label "Select Filter" :href "/sx/(geography.(hypermedia.(example.select-filter)))")
|
||||||
(dict :label "Tabs" :href "/(geography.(hypermedia.(example.tabs)))")
|
(dict :label "Tabs" :href "/sx/(geography.(hypermedia.(example.tabs)))")
|
||||||
(dict :label "Animations" :href "/(geography.(hypermedia.(example.animations)))")
|
(dict :label "Animations" :href "/sx/(geography.(hypermedia.(example.animations)))")
|
||||||
(dict :label "Dialogs" :href "/(geography.(hypermedia.(example.dialogs)))")
|
(dict :label "Dialogs" :href "/sx/(geography.(hypermedia.(example.dialogs)))")
|
||||||
(dict :label "Keyboard Shortcuts" :href "/(geography.(hypermedia.(example.keyboard-shortcuts)))")
|
(dict :label "Keyboard Shortcuts" :href "/sx/(geography.(hypermedia.(example.keyboard-shortcuts)))")
|
||||||
(dict :label "PUT / PATCH" :href "/(geography.(hypermedia.(example.put-patch)))")
|
(dict :label "PUT / PATCH" :href "/sx/(geography.(hypermedia.(example.put-patch)))")
|
||||||
(dict :label "JSON Encoding" :href "/(geography.(hypermedia.(example.json-encoding)))")
|
(dict :label "JSON Encoding" :href "/sx/(geography.(hypermedia.(example.json-encoding)))")
|
||||||
(dict :label "Vals & Headers" :href "/(geography.(hypermedia.(example.vals-and-headers)))")
|
(dict :label "Vals & Headers" :href "/sx/(geography.(hypermedia.(example.vals-and-headers)))")
|
||||||
(dict :label "Loading States" :href "/(geography.(hypermedia.(example.loading-states)))")
|
(dict :label "Loading States" :href "/sx/(geography.(hypermedia.(example.loading-states)))")
|
||||||
(dict :label "Request Abort" :href "/(geography.(hypermedia.(example.sync-replace)))")
|
(dict :label "Request Abort" :href "/sx/(geography.(hypermedia.(example.sync-replace)))")
|
||||||
(dict :label "Retry" :href "/(geography.(hypermedia.(example.retry)))")))
|
(dict :label "Retry" :href "/sx/(geography.(hypermedia.(example.retry)))")))
|
||||||
|
|
||||||
(define cssx-nav-items (list
|
(define cssx-nav-items (list
|
||||||
(dict :label "Overview" :href "/(applications.(cssx))")
|
(dict :label "Overview" :href "/sx/(applications.(cssx))")
|
||||||
(dict :label "Patterns" :href "/(applications.(cssx.patterns))")
|
(dict :label "Patterns" :href "/sx/(applications.(cssx.patterns))")
|
||||||
(dict :label "Delivery" :href "/(applications.(cssx.delivery))")
|
(dict :label "Delivery" :href "/sx/(applications.(cssx.delivery))")
|
||||||
(dict :label "Async CSS" :href "/(applications.(cssx.async))")
|
(dict :label "Async CSS" :href "/sx/(applications.(cssx.async))")
|
||||||
(dict :label "Live Styles" :href "/(applications.(cssx.live))")
|
(dict :label "Live Styles" :href "/sx/(applications.(cssx.live))")
|
||||||
(dict :label "Comparisons" :href "/(applications.(cssx.comparisons))")
|
(dict :label "Comparisons" :href "/sx/(applications.(cssx.comparisons))")
|
||||||
(dict :label "Philosophy" :href "/(applications.(cssx.philosophy))")))
|
(dict :label "Philosophy" :href "/sx/(applications.(cssx.philosophy))")))
|
||||||
|
|
||||||
(define essays-nav-items (list
|
(define essays-nav-items (list
|
||||||
(dict :label "Why S-Expressions" :href "/(etc.(essay.why-sexps))"
|
(dict :label "Why S-Expressions" :href "/sx/(etc.(essay.why-sexps))"
|
||||||
:summary "Why SX uses s-expressions instead of HTML templates, JSX, or any other syntax.")
|
:summary "Why SX uses s-expressions instead of HTML templates, JSX, or any other syntax.")
|
||||||
(dict :label "The htmx/React Hybrid" :href "/(etc.(essay.htmx-react-hybrid))"
|
(dict :label "The htmx/React Hybrid" :href "/sx/(etc.(essay.htmx-react-hybrid))"
|
||||||
:summary "How SX combines the server-driven simplicity of htmx with the component model of React.")
|
:summary "How SX combines the server-driven simplicity of htmx with the component model of React.")
|
||||||
(dict :label "On-Demand CSS" :href "/(etc.(essay.on-demand-css))"
|
(dict :label "On-Demand CSS" :href "/sx/(etc.(essay.on-demand-css))"
|
||||||
:summary "How SX delivers only the CSS each page needs — server scans rendered classes, sends the delta.")
|
:summary "How SX delivers only the CSS each page needs — server scans rendered classes, sends the delta.")
|
||||||
(dict :label "Client Reactivity" :href "/(etc.(essay.client-reactivity))"
|
(dict :label "Client Reactivity" :href "/sx/(etc.(essay.client-reactivity))"
|
||||||
:summary "Reactive UI updates without a virtual DOM, diffing library, or build step.")
|
:summary "Reactive UI updates without a virtual DOM, diffing library, or build step.")
|
||||||
(dict :label "SX Native" :href "/(etc.(essay.sx-native))"
|
(dict :label "SX Native" :href "/sx/(etc.(essay.sx-native))"
|
||||||
:summary "Extending SX beyond the browser — native desktop and mobile rendering from the same source.")
|
:summary "Extending SX beyond the browser — native desktop and mobile rendering from the same source.")
|
||||||
(dict :label "Tail-Call Optimization" :href "/(etc.(essay.tail-call-optimization))"
|
(dict :label "Tail-Call Optimization" :href "/sx/(etc.(essay.tail-call-optimization))"
|
||||||
:summary "How SX implements proper tail calls via trampolining in a language that doesn't have them.")
|
:summary "How SX implements proper tail calls via trampolining in a language that doesn't have them.")
|
||||||
(dict :label "Continuations" :href "/(etc.(essay.continuations))"
|
(dict :label "Continuations" :href "/sx/(etc.(essay.continuations))"
|
||||||
:summary "First-class continuations in a tree-walking evaluator — theory and implementation.")
|
:summary "First-class continuations in a tree-walking evaluator — theory and implementation.")
|
||||||
(dict :label "The Reflexive Web" :href "/(etc.(essay.reflexive-web))"
|
(dict :label "The Reflexive Web" :href "/sx/(etc.(essay.reflexive-web))"
|
||||||
:summary "A web where pages can inspect, modify, and extend their own rendering pipeline.")
|
:summary "A web where pages can inspect, modify, and extend their own rendering pipeline.")
|
||||||
(dict :label "Server Architecture" :href "/(etc.(essay.server-architecture))"
|
(dict :label "Server Architecture" :href "/sx/(etc.(essay.server-architecture))"
|
||||||
:summary "How SX enforces the boundary between host and embedded language, and what it looks like across targets.")
|
:summary "How SX enforces the boundary between host and embedded language, and what it looks like across targets.")
|
||||||
(dict :label "Separate your Own Concerns" :href "/(etc.(essay.separation-of-concerns))"
|
(dict :label "Separate your Own Concerns" :href "/sx/(etc.(essay.separation-of-concerns))"
|
||||||
:summary "The web's HTML/CSS/JS split separates the framework's concerns, not your application's. Real separation is domain-specific.")
|
:summary "The web's HTML/CSS/JS split separates the framework's concerns, not your application's. Real separation is domain-specific.")
|
||||||
(dict :label "SX and AI" :href "/(etc.(essay.sx-and-ai))"
|
(dict :label "SX and AI" :href "/sx/(etc.(essay.sx-and-ai))"
|
||||||
:summary "Why s-expressions are the most AI-friendly representation for web interfaces.")
|
:summary "Why s-expressions are the most AI-friendly representation for web interfaces.")
|
||||||
(dict :label "There Is No Alternative" :href "/(etc.(essay.no-alternative))"
|
(dict :label "There Is No Alternative" :href "/sx/(etc.(essay.no-alternative))"
|
||||||
:summary "Every attempt to escape s-expressions leads back to s-expressions. This is not an accident.")
|
:summary "Every attempt to escape s-expressions leads back to s-expressions. This is not an accident.")
|
||||||
(dict :label "sx sucks" :href "/(etc.(essay.sx-sucks))"
|
(dict :label "sx sucks" :href "/sx/(etc.(essay.sx-sucks))"
|
||||||
:summary "An honest accounting of everything wrong with SX and why you probably shouldn't use it.")
|
:summary "An honest accounting of everything wrong with SX and why you probably shouldn't use it.")
|
||||||
(dict :label "Tools for Fools" :href "/(etc.(essay.zero-tooling))"
|
(dict :label "Tools for Fools" :href "/sx/(etc.(essay.zero-tooling))"
|
||||||
:summary "SX was built without a code editor. No IDE, no build tools, no linters, no bundlers. What zero-tooling web development looks like.")
|
:summary "SX was built without a code editor. No IDE, no build tools, no linters, no bundlers. What zero-tooling web development looks like.")
|
||||||
(dict :label "React is Hypermedia" :href "/(etc.(essay.react-is-hypermedia))"
|
(dict :label "React is Hypermedia" :href "/sx/(etc.(essay.react-is-hypermedia))"
|
||||||
:summary "A React Island is a hypermedia control. Its behavior is specified in SX.")
|
:summary "A React Island is a hypermedia control. Its behavior is specified in SX.")
|
||||||
(dict :label "The Hegelian Synthesis" :href "/(etc.(essay.hegelian-synthesis))"
|
(dict :label "The Hegelian Synthesis" :href "/sx/(etc.(essay.hegelian-synthesis))"
|
||||||
:summary "On the dialectical resolution of the hypertext/reactive contradiction. Thesis: the server renders. Antithesis: the client reacts. Synthesis: the island in the lake.")
|
:summary "On the dialectical resolution of the hypertext/reactive contradiction. Thesis: the server renders. Antithesis: the client reacts. Synthesis: the island in the lake.")
|
||||||
(dict :label "The Art Chain" :href "/(etc.(essay.the-art-chain))"
|
(dict :label "The Art Chain" :href "/sx/(etc.(essay.the-art-chain))"
|
||||||
:summary "On making, self-making, and the chain of artifacts that produces itself. Ars, techne, content addressing, and why the spec is the art.")
|
:summary "On making, self-making, and the chain of artifacts that produces itself. Ars, techne, content addressing, and why the spec is the art.")
|
||||||
(dict :label "The True Hypermedium" :href "/(etc.(essay.self-defining-medium))"
|
(dict :label "The True Hypermedium" :href "/sx/(etc.(essay.self-defining-medium))"
|
||||||
:summary "The true hypermedium must define itself with itself. On ontological uniformity, the metacircular web, and why address and content should be the same stuff.")))
|
:summary "The true hypermedium must define itself with itself. On ontological uniformity, the metacircular web, and why address and content should be the same stuff.")))
|
||||||
|
|
||||||
(define philosophy-nav-items (list
|
(define philosophy-nav-items (list
|
||||||
(dict :label "The SX Manifesto" :href "/(etc.(philosophy.sx-manifesto))"
|
(dict :label "The SX Manifesto" :href "/sx/(etc.(philosophy.sx-manifesto))"
|
||||||
:summary "The design principles behind SX: simplicity, self-hosting, and s-expressions all the way down.")
|
:summary "The design principles behind SX: simplicity, self-hosting, and s-expressions all the way down.")
|
||||||
(dict :label "Strange Loops" :href "/(etc.(philosophy.godel-escher-bach))"
|
(dict :label "Strange Loops" :href "/sx/(etc.(philosophy.godel-escher-bach))"
|
||||||
:summary "Self-reference, and the tangled hierarchy of a language that defines itself.")
|
:summary "Self-reference, and the tangled hierarchy of a language that defines itself.")
|
||||||
(dict :label "SX and Wittgenstein" :href "/(etc.(philosophy.wittgenstein))"
|
(dict :label "SX and Wittgenstein" :href "/sx/(etc.(philosophy.wittgenstein))"
|
||||||
:summary "The limits of my language are the limits of my world — Wittgenstein's philosophy and what it means for SX.")
|
:summary "The limits of my language are the limits of my world — Wittgenstein's philosophy and what it means for SX.")
|
||||||
(dict :label "SX and Dennett" :href "/(etc.(philosophy.dennett))"
|
(dict :label "SX and Dennett" :href "/sx/(etc.(philosophy.dennett))"
|
||||||
:summary "Real patterns, intentional stance, and multiple drafts — Dennett's philosophy of mind as a framework for understanding SX.")
|
:summary "Real patterns, intentional stance, and multiple drafts — Dennett's philosophy of mind as a framework for understanding SX.")
|
||||||
(dict :label "S-Existentialism" :href "/(etc.(philosophy.existentialism))"
|
(dict :label "S-Existentialism" :href "/sx/(etc.(philosophy.existentialism))"
|
||||||
:summary "Existence precedes essence — Sartre, Camus, and the absurd freedom of writing a Lisp for the web.")))
|
:summary "Existence precedes essence — Sartre, Camus, and the absurd freedom of writing a Lisp for the web.")))
|
||||||
|
|
||||||
(define specs-nav-items (list
|
(define specs-nav-items (list
|
||||||
{:label "Core" :href "/(language.(spec.core))" :children (list
|
{:label "Core" :href "/sx/(language.(spec.core))" :children (list
|
||||||
{:label "Parser" :href "/(language.(spec.parser))"}
|
{:label "Parser" :href "/sx/(language.(spec.parser))"}
|
||||||
{:label "Evaluator" :href "/(language.(spec.evaluator))"}
|
{:label "Evaluator" :href "/sx/(language.(spec.evaluator))"}
|
||||||
{:label "Primitives" :href "/(language.(spec.primitives))"}
|
{:label "Primitives" :href "/sx/(language.(spec.primitives))"}
|
||||||
{:label "Special Forms" :href "/(language.(spec.special-forms))"}
|
{:label "Special Forms" :href "/sx/(language.(spec.special-forms))"}
|
||||||
{:label "Renderer" :href "/(language.(spec.renderer))"})}
|
{:label "Renderer" :href "/sx/(language.(spec.renderer))"})}
|
||||||
{:label "Adapters" :href "/(language.(spec.adapters))" :children (list
|
{:label "Adapters" :href "/sx/(language.(spec.adapters))" :children (list
|
||||||
{:label "DOM Adapter" :href "/(language.(spec.adapter-dom))"}
|
{:label "DOM Adapter" :href "/sx/(language.(spec.adapter-dom))"}
|
||||||
{:label "HTML Adapter" :href "/(language.(spec.adapter-html))"}
|
{:label "HTML Adapter" :href "/sx/(language.(spec.adapter-html))"}
|
||||||
{:label "SX Wire Adapter" :href "/(language.(spec.adapter-sx))"}
|
{:label "SX Wire Adapter" :href "/sx/(language.(spec.adapter-sx))"}
|
||||||
{:label "Async Adapter" :href "/(language.(spec.adapter-async))"})}
|
{:label "Async Adapter" :href "/sx/(language.(spec.adapter-async))"})}
|
||||||
{:label "Browser" :href "/(language.(spec.browser))" :children (list
|
{:label "Browser" :href "/sx/(language.(spec.browser))" :children (list
|
||||||
{:label "SxEngine" :href "/(language.(spec.engine))"}
|
{:label "SxEngine" :href "/sx/(language.(spec.engine))"}
|
||||||
{:label "Orchestration" :href "/(language.(spec.orchestration))"}
|
{:label "Orchestration" :href "/sx/(language.(spec.orchestration))"}
|
||||||
{:label "Boot" :href "/(language.(spec.boot))"}
|
{:label "Boot" :href "/sx/(language.(spec.boot))"}
|
||||||
{:label "Router" :href "/(language.(spec.router))"})}
|
{:label "Router" :href "/sx/(language.(spec.router))"})}
|
||||||
{:label "Reactive" :href "/(language.(spec.reactive))" :children (list
|
{:label "Reactive" :href "/sx/(language.(spec.reactive))" :children (list
|
||||||
{:label "Signals" :href "/(language.(spec.signals))"})}
|
{:label "Signals" :href "/sx/(language.(spec.signals))"})}
|
||||||
{:label "Host Interface" :href "/(language.(spec.host))" :children (list
|
{:label "Host Interface" :href "/sx/(language.(spec.host))" :children (list
|
||||||
{:label "Boundary" :href "/(language.(spec.boundary))"}
|
{:label "Boundary" :href "/sx/(language.(spec.boundary))"}
|
||||||
{:label "Forms" :href "/(language.(spec.forms))"}
|
{:label "Forms" :href "/sx/(language.(spec.forms))"}
|
||||||
{:label "Page Helpers" :href "/(language.(spec.page-helpers))"})}
|
{:label "Page Helpers" :href "/sx/(language.(spec.page-helpers))"})}
|
||||||
{:label "Extensions" :href "/(language.(spec.extensions))" :children (list
|
{:label "Extensions" :href "/sx/(language.(spec.extensions))" :children (list
|
||||||
{:label "Continuations" :href "/(language.(spec.continuations))"}
|
{:label "Continuations" :href "/sx/(language.(spec.continuations))"}
|
||||||
{:label "call/cc" :href "/(language.(spec.callcc))"}
|
{:label "call/cc" :href "/sx/(language.(spec.callcc))"}
|
||||||
{:label "Types" :href "/(language.(spec.types))"}
|
{:label "Types" :href "/sx/(language.(spec.types))"}
|
||||||
{:label "Deps" :href "/(language.(spec.deps))"})}))
|
{:label "Deps" :href "/sx/(language.(spec.deps))"})}))
|
||||||
|
|
||||||
(define testing-nav-items (list
|
(define testing-nav-items (list
|
||||||
(dict :label "Overview" :href "/(language.(test))")
|
(dict :label "Overview" :href "/sx/(language.(test))")
|
||||||
(dict :label "Evaluator" :href "/(language.(test.eval))")
|
(dict :label "Evaluator" :href "/sx/(language.(test.eval))")
|
||||||
(dict :label "Parser" :href "/(language.(test.parser))")
|
(dict :label "Parser" :href "/sx/(language.(test.parser))")
|
||||||
(dict :label "Router" :href "/(language.(test.router))")
|
(dict :label "Router" :href "/sx/(language.(test.router))")
|
||||||
(dict :label "Renderer" :href "/(language.(test.render))")
|
(dict :label "Renderer" :href "/sx/(language.(test.render))")
|
||||||
(dict :label "Dependencies" :href "/(language.(test.deps))")
|
(dict :label "Dependencies" :href "/sx/(language.(test.deps))")
|
||||||
(dict :label "Engine" :href "/(language.(test.engine))")
|
(dict :label "Engine" :href "/sx/(language.(test.engine))")
|
||||||
(dict :label "Orchestration" :href "/(language.(test.orchestration))")
|
(dict :label "Orchestration" :href "/sx/(language.(test.orchestration))")
|
||||||
(dict :label "Runners" :href "/(language.(test.runners))")))
|
(dict :label "Runners" :href "/sx/(language.(test.runners))")))
|
||||||
|
|
||||||
(define isomorphism-nav-items (list
|
(define isomorphism-nav-items (list
|
||||||
(dict :label "Roadmap" :href "/(geography.(isomorphism))")
|
(dict :label "Roadmap" :href "/sx/(geography.(isomorphism))")
|
||||||
(dict :label "Bundle Analyzer" :href "/(geography.(isomorphism.bundle-analyzer))")
|
(dict :label "Bundle Analyzer" :href "/sx/(geography.(isomorphism.bundle-analyzer))")
|
||||||
(dict :label "Routing Analyzer" :href "/(geography.(isomorphism.routing-analyzer))")
|
(dict :label "Routing Analyzer" :href "/sx/(geography.(isomorphism.routing-analyzer))")
|
||||||
(dict :label "Data Test" :href "/(geography.(isomorphism.data-test))")
|
(dict :label "Data Test" :href "/sx/(geography.(isomorphism.data-test))")
|
||||||
(dict :label "Async IO" :href "/(geography.(isomorphism.async-io))")
|
(dict :label "Async IO" :href "/sx/(geography.(isomorphism.async-io))")
|
||||||
(dict :label "Streaming" :href "/(geography.(isomorphism.streaming))")
|
(dict :label "Streaming" :href "/sx/(geography.(isomorphism.streaming))")
|
||||||
(dict :label "Affinity" :href "/(geography.(isomorphism.affinity))")
|
(dict :label "Affinity" :href "/sx/(geography.(isomorphism.affinity))")
|
||||||
(dict :label "Optimistic" :href "/(geography.(isomorphism.optimistic))")
|
(dict :label "Optimistic" :href "/sx/(geography.(isomorphism.optimistic))")
|
||||||
(dict :label "Offline" :href "/(geography.(isomorphism.offline))")))
|
(dict :label "Offline" :href "/sx/(geography.(isomorphism.offline))")))
|
||||||
|
|
||||||
(define plans-nav-items (list
|
(define plans-nav-items (list
|
||||||
(dict :label "Status" :href "/(etc.(plan.status))"
|
(dict :label "Status" :href "/sx/(etc.(plan.status))"
|
||||||
:summary "Audit of all plans — what's done, what's in progress, and what remains.")
|
:summary "Audit of all plans — what's done, what's in progress, and what remains.")
|
||||||
(dict :label "Reader Macros" :href "/(etc.(plan.reader-macros))"
|
(dict :label "Reader Macros" :href "/sx/(etc.(plan.reader-macros))"
|
||||||
:summary "Extensible parse-time transformations via # dispatch — datum comments, raw strings, and quote shorthand.")
|
:summary "Extensible parse-time transformations via # dispatch — datum comments, raw strings, and quote shorthand.")
|
||||||
(dict :label "Reader Macro Demo" :href "/(etc.(plan.reader-macro-demo))"
|
(dict :label "Reader Macro Demo" :href "/sx/(etc.(plan.reader-macro-demo))"
|
||||||
:summary "Live demo: #z3 translates SX spec declarations to SMT-LIB verification conditions.")
|
:summary "Live demo: #z3 translates SX spec declarations to SMT-LIB verification conditions.")
|
||||||
(dict :label "Theorem Prover" :href "/(etc.(plan.theorem-prover))"
|
(dict :label "Theorem Prover" :href "/sx/(etc.(plan.theorem-prover))"
|
||||||
:summary "prove.sx — constraint solver and property prover for SX primitives, written in SX.")
|
:summary "prove.sx — constraint solver and property prover for SX primitives, written in SX.")
|
||||||
(dict :label "Self-Hosting Bootstrapper" :href "/(etc.(plan.self-hosting-bootstrapper))"
|
(dict :label "Self-Hosting Bootstrapper" :href "/sx/(etc.(plan.self-hosting-bootstrapper))"
|
||||||
:summary "py.sx — an SX-to-Python translator written in SX. Complete: G0 == G1, 128/128 defines match.")
|
:summary "py.sx — an SX-to-Python translator written in SX. Complete: G0 == G1, 128/128 defines match.")
|
||||||
(dict :label "JS Bootstrapper" :href "/(etc.(plan.js-bootstrapper))"
|
(dict :label "JS Bootstrapper" :href "/sx/(etc.(plan.js-bootstrapper))"
|
||||||
:summary "js.sx — SX-to-JavaScript translator + ahead-of-time component compiler. Zero-runtime static sites.")
|
:summary "js.sx — SX-to-JavaScript translator + ahead-of-time component compiler. Zero-runtime static sites.")
|
||||||
(dict :label "SX-Activity" :href "/(etc.(plan.sx-activity))"
|
(dict :label "SX-Activity" :href "/sx/(etc.(plan.sx-activity))"
|
||||||
:summary "A new web built on SX — executable content, shared components, parsers, and logic on IPFS, provenance on Bitcoin, all running within your own security context.")
|
:summary "A new web built on SX — executable content, shared components, parsers, and logic on IPFS, provenance on Bitcoin, all running within your own security context.")
|
||||||
(dict :label "Predictive Prefetching" :href "/(etc.(plan.predictive-prefetch))"
|
(dict :label "Predictive Prefetching" :href "/sx/(etc.(plan.predictive-prefetch))"
|
||||||
:summary "Prefetch missing component definitions before the user clicks — hover a link, fetch its deps, navigate client-side.")
|
:summary "Prefetch missing component definitions before the user clicks — hover a link, fetch its deps, navigate client-side.")
|
||||||
(dict :label "Content-Addressed Components" :href "/(etc.(plan.content-addressed-components))"
|
(dict :label "Content-Addressed Components" :href "/sx/(etc.(plan.content-addressed-components))"
|
||||||
:summary "Components identified by CID, stored on IPFS, fetched from anywhere. Canonical serialization, content verification, federated sharing.")
|
:summary "Components identified by CID, stored on IPFS, fetched from anywhere. Canonical serialization, content verification, federated sharing.")
|
||||||
(dict :label "Environment Images" :href "/(etc.(plan.environment-images))"
|
(dict :label "Environment Images" :href "/sx/(etc.(plan.environment-images))"
|
||||||
:summary "Serialize evaluated environments as content-addressed images. Spec CID → image CID → every endpoint is fully executable and verifiable.")
|
:summary "Serialize evaluated environments as content-addressed images. Spec CID → image CID → every endpoint is fully executable and verifiable.")
|
||||||
(dict :label "Runtime Slicing" :href "/(etc.(plan.runtime-slicing))"
|
(dict :label "Runtime Slicing" :href "/sx/(etc.(plan.runtime-slicing))"
|
||||||
:summary "Tier the client runtime by need: L0 hypermedia (~5KB), L1 DOM ops (~8KB), L2 islands (~15KB), L3 full eval (~44KB). Sliced by slice.sx, translated by js.sx.")
|
:summary "Tier the client runtime by need: L0 hypermedia (~5KB), L1 DOM ops (~8KB), L2 islands (~15KB), L3 full eval (~44KB). Sliced by slice.sx, translated by js.sx.")
|
||||||
(dict :label "Typed SX" :href "/(etc.(plan.typed-sx))"
|
(dict :label "Typed SX" :href "/sx/(etc.(plan.typed-sx))"
|
||||||
:summary "Gradual type system with static effect checking. Optional type annotations, deftype (aliases, unions, records), and effect declarations — checked at registration time, zero runtime cost. types.sx — specced, bootstrapped, catches composition and boundary errors.")
|
:summary "Gradual type system with static effect checking. Optional type annotations, deftype (aliases, unions, records), and effect declarations — checked at registration time, zero runtime cost. types.sx — specced, bootstrapped, catches composition and boundary errors.")
|
||||||
(dict :label "Nav Redesign" :href "/(etc.(plan.nav-redesign))"
|
(dict :label "Nav Redesign" :href "/sx/(etc.(plan.nav-redesign))"
|
||||||
:summary "Replace menu bars with vertical breadcrumb navigation. Logo → section → page, arrows for siblings, children below. No dropdowns, no hamburger, infinite depth.")
|
:summary "Replace menu bars with vertical breadcrumb navigation. Logo → section → page, arrows for siblings, children below. No dropdowns, no hamburger, infinite depth.")
|
||||||
(dict :label "Fragment Protocol" :href "/(etc.(plan.fragment-protocol))"
|
(dict :label "Fragment Protocol" :href "/sx/(etc.(plan.fragment-protocol))"
|
||||||
:summary "Structured sexp request/response for cross-service component transfer.")
|
:summary "Structured sexp request/response for cross-service component transfer.")
|
||||||
(dict :label "Glue Decoupling" :href "/(etc.(plan.glue-decoupling))"
|
(dict :label "Glue Decoupling" :href "/sx/(etc.(plan.glue-decoupling))"
|
||||||
:summary "Eliminate all cross-app model imports via glue service layer.")
|
:summary "Eliminate all cross-app model imports via glue service layer.")
|
||||||
(dict :label "Social Sharing" :href "/(etc.(plan.social-sharing))"
|
(dict :label "Social Sharing" :href "/sx/(etc.(plan.social-sharing))"
|
||||||
:summary "OAuth-based sharing to Facebook, Instagram, Threads, Twitter/X, LinkedIn, and Mastodon.")
|
:summary "OAuth-based sharing to Facebook, Instagram, Threads, Twitter/X, LinkedIn, and Mastodon.")
|
||||||
(dict :label "SX CI Pipeline" :href "/(etc.(plan.sx-ci))"
|
(dict :label "SX CI Pipeline" :href "/sx/(etc.(plan.sx-ci))"
|
||||||
:summary "Build, test, and deploy in s-expressions — CI pipelines as SX components.")
|
:summary "Build, test, and deploy in s-expressions — CI pipelines as SX components.")
|
||||||
(dict :label "Live Streaming" :href "/(etc.(plan.live-streaming))"
|
(dict :label "Live Streaming" :href "/sx/(etc.(plan.live-streaming))"
|
||||||
:summary "SSE and WebSocket transports for re-resolving suspense slots after initial page load — live data, real-time collaboration.")
|
:summary "SSE and WebSocket transports for re-resolving suspense slots after initial page load — live data, real-time collaboration.")
|
||||||
(dict :label "sx-web Platform" :href "/(etc.(plan.sx-web-platform))"
|
(dict :label "sx-web Platform" :href "/sx/(etc.(plan.sx-web-platform))"
|
||||||
:summary "sx-web.org as online development platform — embedded Claude Code, IPFS storage, sx-activity publishing, sx-ci testing. Author, stage, test, deploy from the browser.")
|
:summary "sx-web.org as online development platform — embedded Claude Code, IPFS storage, sx-activity publishing, sx-ci testing. Author, stage, test, deploy from the browser.")
|
||||||
(dict :label "sx-forge" :href "/(etc.(plan.sx-forge))"
|
(dict :label "sx-forge" :href "/sx/(etc.(plan.sx-forge))"
|
||||||
:summary "Git forge in SX — repositories, issues, pull requests, CI, permissions, and federation. Configuration as macros, diffs as components.")
|
:summary "Git forge in SX — repositories, issues, pull requests, CI, permissions, and federation. Configuration as macros, diffs as components.")
|
||||||
(dict :label "sx-swarm" :href "/(etc.(plan.sx-swarm))"
|
(dict :label "sx-swarm" :href "/sx/(etc.(plan.sx-swarm))"
|
||||||
:summary "Container orchestration in SX — service definitions, environment macros, deploy pipelines. Replace YAML with a real language.")
|
:summary "Container orchestration in SX — service definitions, environment macros, deploy pipelines. Replace YAML with a real language.")
|
||||||
(dict :label "sx-proxy" :href "/(etc.(plan.sx-proxy))"
|
(dict :label "sx-proxy" :href "/sx/(etc.(plan.sx-proxy))"
|
||||||
:summary "Reverse proxy in SX — routes, TLS, middleware chains, load balancing. Macros generate config from the same service definitions as the orchestrator.")
|
:summary "Reverse proxy in SX — routes, TLS, middleware chains, load balancing. Macros generate config from the same service definitions as the orchestrator.")
|
||||||
(dict :label "Async Eval Convergence" :href "/(etc.(plan.async-eval-convergence))"
|
(dict :label "Async Eval Convergence" :href "/sx/(etc.(plan.async-eval-convergence))"
|
||||||
:summary "Eliminate hand-written evaluators — bootstrap async_eval.py from the spec via an async adapter layer. One spec, one truth, zero divergence.")
|
:summary "Eliminate hand-written evaluators — bootstrap async_eval.py from the spec via an async adapter layer. One spec, one truth, zero divergence.")
|
||||||
(dict :label "WASM Bytecode VM" :href "/(etc.(plan.wasm-bytecode-vm))"
|
(dict :label "WASM Bytecode VM" :href "/sx/(etc.(plan.wasm-bytecode-vm))"
|
||||||
:summary "Compile SX to bytecode, run in a Rust/WASM VM. Compact wire format, no parse overhead, near-native speed, DOM via JS bindings.")
|
:summary "Compile SX to bytecode, run in a Rust/WASM VM. Compact wire format, no parse overhead, near-native speed, DOM via JS bindings.")
|
||||||
(dict :label "Generative SX" :href "/(etc.(plan.generative-sx))"
|
(dict :label "Generative SX" :href "/sx/(etc.(plan.generative-sx))"
|
||||||
:summary "Programs that write themselves as they run — self-compiling specs, runtime self-extension, generative testing, seed networks.")
|
:summary "Programs that write themselves as they run — self-compiling specs, runtime self-extension, generative testing, seed networks.")
|
||||||
(dict :label "Art DAG on SX" :href "/(etc.(plan.art-dag-sx))"
|
(dict :label "Art DAG on SX" :href "/sx/(etc.(plan.art-dag-sx))"
|
||||||
:summary "SX endpoints as portals into media processing environments — recipes as programs, split execution across GPU/cache/live boundaries, streaming AV output.")
|
:summary "SX endpoints as portals into media processing environments — recipes as programs, split execution across GPU/cache/live boundaries, streaming AV output.")
|
||||||
(dict :label "Spec Explorer" :href "/(etc.(plan.spec-explorer))"
|
(dict :label "Spec Explorer" :href "/sx/(etc.(plan.spec-explorer))"
|
||||||
:summary "The fifth ring — SX exploring itself. Per-function cards showing source, Python/JS/Z3 translations, platform dependencies, tests, proofs, and usage examples.")
|
:summary "The fifth ring — SX exploring itself. Per-function cards showing source, Python/JS/Z3 translations, platform dependencies, tests, proofs, and usage examples.")
|
||||||
(dict :label "SX Protocol" :href "/(etc.(plan.sx-protocol))"
|
(dict :label "SX Protocol" :href "/sx/(etc.(plan.sx-protocol))"
|
||||||
:summary "S-expressions as a universal protocol for networked hypermedia — replacing URLs, HTTP verbs, query languages, and rendering with one evaluable format.")))
|
:summary "S-expressions as a universal protocol for networked hypermedia — replacing URLs, HTTP verbs, query languages, and rendering with one evaluable format.")))
|
||||||
|
|
||||||
(define reactive-islands-nav-items (list
|
(define reactive-islands-nav-items (list
|
||||||
(dict :label "Overview" :href "/(geography.(reactive))"
|
(dict :label "Overview" :href "/sx/(geography.(reactive))"
|
||||||
:summary "Architecture, four levels (L0-L3), and current implementation status.")
|
:summary "Architecture, four levels (L0-L3), and current implementation status.")
|
||||||
(dict :label "Demo" :href "/(geography.(reactive.demo))"
|
(dict :label "Demo" :href "/sx/(geography.(reactive.demo))"
|
||||||
:summary "Live demonstration of signals, computed, effects, batch, and defisland — all transpiled from spec.")
|
:summary "Live demonstration of signals, computed, effects, batch, and defisland — all transpiled from spec.")
|
||||||
(dict :label "Event Bridge" :href "/(geography.(reactive.event-bridge))"
|
(dict :label "Event Bridge" :href "/sx/(geography.(reactive.event-bridge))"
|
||||||
:summary "DOM events for htmx lake → island communication. Server-rendered buttons dispatch custom events that island effects listen for.")
|
:summary "DOM events for htmx lake → island communication. Server-rendered buttons dispatch custom events that island effects listen for.")
|
||||||
(dict :label "Named Stores" :href "/(geography.(reactive.named-stores))"
|
(dict :label "Named Stores" :href "/sx/(geography.(reactive.named-stores))"
|
||||||
:summary "Page-level signal containers via def-store/use-store — persist across island destruction/recreation.")
|
:summary "Page-level signal containers via def-store/use-store — persist across island destruction/recreation.")
|
||||||
(dict :label "Plan" :href "/(geography.(reactive.plan))"
|
(dict :label "Plan" :href "/sx/(geography.(reactive.plan))"
|
||||||
:summary "The full design document — rendering boundary, state flow, signal primitives, island lifecycle.")
|
:summary "The full design document — rendering boundary, state flow, signal primitives, island lifecycle.")
|
||||||
(dict :label "Phase 2" :href "/(geography.(reactive.phase2))"
|
(dict :label "Phase 2" :href "/sx/(geography.(reactive.phase2))"
|
||||||
:summary "Input binding, keyed lists, reactive class/style, refs, portals, error boundaries, suspense, transitions.")))
|
:summary "Input binding, keyed lists, reactive class/style, refs, portals, error boundaries, suspense, transitions.")))
|
||||||
|
|
||||||
(define bootstrappers-nav-items (list
|
(define bootstrappers-nav-items (list
|
||||||
(dict :label "Overview" :href "/(language.(bootstrapper))")
|
(dict :label "Overview" :href "/sx/(language.(bootstrapper))")
|
||||||
(dict :label "JavaScript" :href "/(language.(bootstrapper.javascript))")
|
(dict :label "JavaScript" :href "/sx/(language.(bootstrapper.javascript))")
|
||||||
(dict :label "Python" :href "/(language.(bootstrapper.python))")
|
(dict :label "Python" :href "/sx/(language.(bootstrapper.python))")
|
||||||
(dict :label "Self-Hosting (py.sx)" :href "/(language.(bootstrapper.self-hosting))")
|
(dict :label "Self-Hosting (py.sx)" :href "/sx/(language.(bootstrapper.self-hosting))")
|
||||||
(dict :label "Self-Hosting JS (js.sx)" :href "/(language.(bootstrapper.self-hosting-js))")
|
(dict :label "Self-Hosting JS (js.sx)" :href "/sx/(language.(bootstrapper.self-hosting-js))")
|
||||||
(dict :label "Page Helpers" :href "/(language.(bootstrapper.page-helpers))")))
|
(dict :label "Page Helpers" :href "/sx/(language.(bootstrapper.page-helpers))")))
|
||||||
|
|
||||||
;; Spec file registry — canonical metadata for spec viewer pages.
|
;; Spec file registry — canonical metadata for spec viewer pages.
|
||||||
;; Python only handles file I/O (read-spec-file); all metadata lives here.
|
;; Python only handles file I/O (read-spec-file); all metadata lives here.
|
||||||
@@ -359,34 +359,34 @@
|
|||||||
;; ---------------------------------------------------------------------------
|
;; ---------------------------------------------------------------------------
|
||||||
|
|
||||||
(define sx-nav-tree
|
(define sx-nav-tree
|
||||||
{:label "sx" :href "/"
|
{:label "sx" :href "/sx/"
|
||||||
:children (list
|
:children (list
|
||||||
{:label "Geography" :href "/(geography)"
|
{:label "Geography" :href "/sx/(geography)"
|
||||||
:children (list
|
:children (list
|
||||||
{:label "Reactive Islands" :href "/(geography.(reactive))" :children reactive-islands-nav-items}
|
{:label "Reactive Islands" :href "/sx/(geography.(reactive))" :children reactive-islands-nav-items}
|
||||||
{:label "Hypermedia Lakes" :href "/(geography.(hypermedia))"
|
{:label "Hypermedia Lakes" :href "/sx/(geography.(hypermedia))"
|
||||||
:children (list
|
:children (list
|
||||||
{:label "Reference" :href "/(geography.(hypermedia.(reference)))" :children reference-nav-items}
|
{:label "Reference" :href "/sx/(geography.(hypermedia.(reference)))" :children reference-nav-items}
|
||||||
{:label "Examples" :href "/(geography.(hypermedia.(example)))" :children examples-nav-items})}
|
{:label "Examples" :href "/sx/(geography.(hypermedia.(example)))" :children examples-nav-items})}
|
||||||
{:label "Marshes" :href "/(geography.(marshes))"
|
{:label "Marshes" :href "/sx/(geography.(marshes))"
|
||||||
:summary "Where reactivity and hypermedia interpenetrate — server writes to signals, reactive transforms reshape server content, client state modifies how hypermedia is interpreted."}
|
:summary "Where reactivity and hypermedia interpenetrate — server writes to signals, reactive transforms reshape server content, client state modifies how hypermedia is interpreted."}
|
||||||
{:label "Isomorphism" :href "/(geography.(isomorphism))" :children isomorphism-nav-items})}
|
{:label "Isomorphism" :href "/sx/(geography.(isomorphism))" :children isomorphism-nav-items})}
|
||||||
{:label "Language" :href "/(language)"
|
{:label "Language" :href "/sx/(language)"
|
||||||
:children (list
|
:children (list
|
||||||
{:label "Docs" :href "/(language.(doc))" :children docs-nav-items}
|
{:label "Docs" :href "/sx/(language.(doc))" :children docs-nav-items}
|
||||||
{:label "Specs" :href "/(language.(spec))" :children specs-nav-items}
|
{:label "Specs" :href "/sx/(language.(spec))" :children specs-nav-items}
|
||||||
{:label "Bootstrappers" :href "/(language.(bootstrapper))" :children bootstrappers-nav-items}
|
{:label "Bootstrappers" :href "/sx/(language.(bootstrapper))" :children bootstrappers-nav-items}
|
||||||
{:label "Testing" :href "/(language.(test))" :children testing-nav-items})}
|
{:label "Testing" :href "/sx/(language.(test))" :children testing-nav-items})}
|
||||||
{:label "Applications" :href "/(applications)"
|
{:label "Applications" :href "/sx/(applications)"
|
||||||
:children (list
|
:children (list
|
||||||
{:label "SX URLs" :href "/(applications.(sx-urls))"}
|
{:label "SX URLs" :href "/sx/(applications.(sx-urls))"}
|
||||||
{:label "CSSX" :href "/(applications.(cssx))" :children cssx-nav-items}
|
{:label "CSSX" :href "/sx/(applications.(cssx))" :children cssx-nav-items}
|
||||||
{:label "Protocols" :href "/(applications.(protocol))" :children protocols-nav-items})}
|
{:label "Protocols" :href "/sx/(applications.(protocol))" :children protocols-nav-items})}
|
||||||
{:label "Etc" :href "/(etc)"
|
{:label "Etc" :href "/sx/(etc)"
|
||||||
:children (list
|
:children (list
|
||||||
{:label "Essays" :href "/(etc.(essay))" :children essays-nav-items}
|
{:label "Essays" :href "/sx/(etc.(essay))" :children essays-nav-items}
|
||||||
{:label "Philosophy" :href "/(etc.(philosophy))" :children philosophy-nav-items}
|
{:label "Philosophy" :href "/sx/(etc.(philosophy))" :children philosophy-nav-items}
|
||||||
{:label "Plans" :href "/(etc.(plan))" :children plans-nav-items})})})
|
{:label "Plans" :href "/sx/(etc.(plan))" :children plans-nav-items})})})
|
||||||
|
|
||||||
;; ---------------------------------------------------------------------------
|
;; ---------------------------------------------------------------------------
|
||||||
;; Nav resolution — tree-descent matching (no prefix heuristics)
|
;; Nav resolution — tree-descent matching (no prefix heuristics)
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
(p :class "mt-2"
|
(p :class "mt-2"
|
||||||
:style (cssx (:text (colour "stone" 400) (size "sm") (family "mono")))
|
:style (cssx (:text (colour "stone" 400) (size "sm") (family "mono")))
|
||||||
path))
|
path))
|
||||||
(a :href "/"
|
(a :href "/sx/"
|
||||||
:sx-get "/" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
:class "inline-block mt-6 px-4 py-2 rounded border transition-colors"
|
:class "inline-block mt-6 px-4 py-2 rounded border transition-colors"
|
||||||
:style (cssx (:text (colour "violet" 700) (size "sm"))
|
:style (cssx (:text (colour "violet" 700) (size "sm"))
|
||||||
|
|||||||
@@ -193,7 +193,7 @@
|
|||||||
(map (fn (item)
|
(map (fn (item)
|
||||||
(dict :title (get item "title") :desc (get item "desc")
|
(dict :title (get item "title") :desc (get item "desc")
|
||||||
:prose (get item "prose")
|
:prose (get item "prose")
|
||||||
:filename (get item "filename") :href (str "/(language.(spec." (get item "slug") "))")
|
:filename (get item "filename") :href (str "/sx/(language.(spec." (get item "slug") "))")
|
||||||
:source (read-spec-file (get item "filename"))))
|
:source (read-spec-file (get item "filename"))))
|
||||||
items)))
|
items)))
|
||||||
|
|
||||||
|
|||||||
@@ -280,7 +280,7 @@
|
|||||||
(~doc-subsection :title "CID References in Page Registry"
|
(~doc-subsection :title "CID References in Page Registry"
|
||||||
(p "The page registry (shipped to the client as " (code "<script type=\"text/sx-pages\">") ") currently lists deps by name. Extend to include CIDs:")
|
(p "The page registry (shipped to the client as " (code "<script type=\"text/sx-pages\">") ") currently lists deps by name. Extend to include CIDs:")
|
||||||
(~doc-code :code (highlight "{:name \"docs-page\" :path \"/language/docs/<slug>\"\n :auth \"public\" :has-data false\n :deps ({:name \"~essay-foo\" :cid \"bafy...essay\"}\n {:name \"~doc-code\" :cid \"bafy...doccode\"})\n :content \"(case slug ...)\" :closure {}}" "lisp"))
|
(~doc-code :code (highlight "{:name \"docs-page\" :path \"/language/docs/<slug>\"\n :auth \"public\" :has-data false\n :deps ({:name \"~essay-foo\" :cid \"bafy...essay\"}\n {:name \"~doc-code\" :cid \"bafy...doccode\"})\n :content \"(case slug ...)\" :closure {}}" "lisp"))
|
||||||
(p "The " (a :href "/(etc.(plan.predictive-prefetch))" :class "text-violet-700 underline" "predictive prefetch system") " uses these CIDs to fetch components from the resolution cascade rather than only from the origin server's " (code "/sx/components") " endpoint."))
|
(p "The " (a :href "/sx/(etc.(plan.predictive-prefetch))" :class "text-violet-700 underline" "predictive prefetch system") " uses these CIDs to fetch components from the resolution cascade rather than only from the origin server's " (code "/sx/components") " endpoint."))
|
||||||
|
|
||||||
(~doc-subsection :title "SX Response Component Headers"
|
(~doc-subsection :title "SX Response Component Headers"
|
||||||
(p "Currently, " (code "SX-Components") " header lists loaded component names. Extend to support CIDs:")
|
(p "Currently, " (code "SX-Components") " header lists loaded component names. Extend to support CIDs:")
|
||||||
@@ -407,9 +407,9 @@
|
|||||||
(~doc-section :title "Relationships" :id "relationships"
|
(~doc-section :title "Relationships" :id "relationships"
|
||||||
(p "This plan is the foundation for several other plans and roadmaps:")
|
(p "This plan is the foundation for several other plans and roadmaps:")
|
||||||
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
||||||
(li (a :href "/(etc.(plan.sx-activity))" :class "text-violet-700 underline" "SX-Activity") " Phase 2 (content-addressed components on IPFS) is a summary of this plan. This plan supersedes that section with full detail.")
|
(li (a :href "/sx/(etc.(plan.sx-activity))" :class "text-violet-700 underline" "SX-Activity") " Phase 2 (content-addressed components on IPFS) is a summary of this plan. This plan supersedes that section with full detail.")
|
||||||
(li (a :href "/(etc.(plan.predictive-prefetch))" :class "text-violet-700 underline" "Predictive prefetching") " gains CID-based resolution — the " (code "/sx/components") " endpoint and IPFS gateway become alternative resolution paths in the prefetch cascade.")
|
(li (a :href "/sx/(etc.(plan.predictive-prefetch))" :class "text-violet-700 underline" "Predictive prefetching") " gains CID-based resolution — the " (code "/sx/components") " endpoint and IPFS gateway become alternative resolution paths in the prefetch cascade.")
|
||||||
(li (a :href "/(etc.(plan.isomorphic-architecture))" :class "text-violet-700 underline" "Isomorphic architecture") " Phase 1 (component distribution) is enhanced — CIDs make per-page bundles verifiable and cross-server shareable.")
|
(li (a :href "/sx/(etc.(plan.isomorphic-architecture))" :class "text-violet-700 underline" "Isomorphic architecture") " Phase 1 (component distribution) is enhanced — CIDs make per-page bundles verifiable and cross-server shareable.")
|
||||||
(li "The SX-Activity vision of " (strong "serverless applications on IPFS") " depends entirely on this plan. Without content-addressed components, applications can't be pinned to IPFS as self-contained artifacts."))
|
(li "The SX-Activity vision of " (strong "serverless applications on IPFS") " depends entirely on this plan. Without content-addressed components, applications can't be pinned to IPFS as self-contained artifacts."))
|
||||||
(div :class "rounded border border-amber-200 bg-amber-50 p-3 mt-2"
|
(div :class "rounded border border-amber-200 bg-amber-50 p-3 mt-2"
|
||||||
(p :class "text-amber-800 text-sm" (strong "Depends on: ") "deps.sx (complete), boundary enforcement (complete), IPFS infrastructure (exists in artdag, needs wiring to web platform)."))))))
|
(p :class "text-amber-800 text-sm" (strong "Depends on: ") "deps.sx (complete), boundary enforcement (complete), IPFS infrastructure (exists in artdag, needs wiring to web platform)."))))))
|
||||||
|
|||||||
@@ -278,11 +278,11 @@
|
|||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 text-stone-700" "Canonical serialization")
|
(td :class "px-3 py-2 text-stone-700" "Canonical serialization")
|
||||||
(td :class "px-3 py-2 text-stone-700" (span :class "text-red-700 font-medium" "Not started"))
|
(td :class "px-3 py-2 text-stone-700" (span :class "text-red-700 font-medium" "Not started"))
|
||||||
(td :class "px-3 py-2" (a :href "/(etc.(plan.content-addressed-components))" :class "text-violet-700 underline" "Content-Addressed Components") " Phase 1"))
|
(td :class "px-3 py-2" (a :href "/sx/(etc.(plan.content-addressed-components))" :class "text-violet-700 underline" "Content-Addressed Components") " Phase 1"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 text-stone-700" "Component CIDs")
|
(td :class "px-3 py-2 text-stone-700" "Component CIDs")
|
||||||
(td :class "px-3 py-2 text-stone-700" (span :class "text-red-700 font-medium" "Not started"))
|
(td :class "px-3 py-2 text-stone-700" (span :class "text-red-700 font-medium" "Not started"))
|
||||||
(td :class "px-3 py-2" (a :href "/(etc.(plan.content-addressed-components))" :class "text-violet-700 underline" "Content-Addressed Components") " Phase 2"))
|
(td :class "px-3 py-2" (a :href "/sx/(etc.(plan.content-addressed-components))" :class "text-violet-700 underline" "Content-Addressed Components") " Phase 2"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 text-stone-700" "Purity verification")
|
(td :class "px-3 py-2 text-stone-700" "Purity verification")
|
||||||
(td :class "px-3 py-2 text-stone-700" (span :class "text-green-700 font-medium" "Complete"))
|
(td :class "px-3 py-2 text-stone-700" (span :class "text-green-700 font-medium" "Complete"))
|
||||||
@@ -301,4 +301,4 @@
|
|||||||
(td :class "px-3 py-2 text-stone-600" "artdag L1/L2, IPFSPin model")))))
|
(td :class "px-3 py-2 text-stone-600" "artdag L1/L2, IPFSPin model")))))
|
||||||
|
|
||||||
(div :class "rounded border border-amber-200 bg-amber-50 p-3 mt-2"
|
(div :class "rounded border border-amber-200 bg-amber-50 p-3 mt-2"
|
||||||
(p :class "text-amber-800 text-sm" (strong "Builds on: ") (a :href "/(etc.(plan.content-addressed-components))" :class "underline" "Content-Addressed Components") " (canonical serialization + CIDs), " (a :href "/(etc.(plan.self-hosting-bootstrapper))" :class "underline" "self-hosting bootstrappers") " (spec-first architecture). " (strong "Enables: ") (a :href "/(etc.(plan.sx-activity))" :class "underline" "SX-Activity") " (serverless applications on IPFS).")))))
|
(p :class "text-amber-800 text-sm" (strong "Builds on: ") (a :href "/sx/(etc.(plan.content-addressed-components))" :class "underline" "Content-Addressed Components") " (canonical serialization + CIDs), " (a :href "/sx/(etc.(plan.self-hosting-bootstrapper))" :class "underline" "self-hosting bootstrappers") " (spec-first architecture). " (strong "Enables: ") (a :href "/sx/(etc.(plan.sx-activity))" :class "underline" "SX-Activity") " (serverless applications on IPFS).")))))
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
(~doc-section :title "Context" :id "context"
|
(~doc-section :title "Context" :id "context"
|
||||||
(p "SX has a working server-client pipeline: server evaluates pages with IO (DB, fragments), serializes as SX wire format, client parses and renders to DOM. The language and primitives are already isomorphic " (em "— same spec, same semantics, both sides.") " What's missing is the " (strong "plumbing") " that makes the boundary between server and client a sliding window rather than a fixed wall.")
|
(p "SX has a working server-client pipeline: server evaluates pages with IO (DB, fragments), serializes as SX wire format, client parses and renders to DOM. The language and primitives are already isomorphic " (em "— same spec, same semantics, both sides.") " What's missing is the " (strong "plumbing") " that makes the boundary between server and client a sliding window rather than a fixed wall.")
|
||||||
(p "The key insight: " (strong "s-expressions can partially unfold on the server after IO, then finish unfolding on the client.") " The system knows which components have data fetches (via IO detection in " (a :href "/(language.(spec.deps))" :class "text-violet-700 underline" "deps.sx") "), resolves those server-side, and sends the rest as pure SX for client rendering. The boundary slides automatically based on what each component actually needs."))
|
(p "The key insight: " (strong "s-expressions can partially unfold on the server after IO, then finish unfolding on the client.") " The system knows which components have data fetches (via IO detection in " (a :href "/sx/(language.(spec.deps))" :class "text-violet-700 underline" "deps.sx") "), resolves those server-side, and sends the rest as pure SX for client rendering. The boundary slides automatically based on what each component actually needs."))
|
||||||
|
|
||||||
(~doc-section :title "Current State" :id "current-state"
|
(~doc-section :title "Current State" :id "current-state"
|
||||||
(ul :class "space-y-2 text-stone-700 list-disc pl-5"
|
(ul :class "space-y-2 text-stone-700 list-disc pl-5"
|
||||||
@@ -32,8 +32,8 @@
|
|||||||
(div :class "rounded border border-green-300 bg-green-50 p-4 mb-4"
|
(div :class "rounded border border-green-300 bg-green-50 p-4 mb-4"
|
||||||
(div :class "flex items-center gap-2 mb-2"
|
(div :class "flex items-center gap-2 mb-2"
|
||||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||||
(a :href "/(language.(spec.deps))" :class "text-green-700 underline text-sm font-medium" "View canonical spec: deps.sx")
|
(a :href "/sx/(language.(spec.deps))" :class "text-green-700 underline text-sm font-medium" "View canonical spec: deps.sx")
|
||||||
(a :href "/(geography.(isomorphism.bundle-analyzer))" :class "text-green-700 underline text-sm font-medium" "Live bundle analyzer"))
|
(a :href "/sx/(geography.(isomorphism.bundle-analyzer))" :class "text-green-700 underline text-sm font-medium" "Live bundle analyzer"))
|
||||||
(p :class "text-green-900 font-medium" "What it enables")
|
(p :class "text-green-900 font-medium" "What it enables")
|
||||||
(p :class "text-green-800" "Per-page component bundles instead of sending every definition to every page. Smaller payloads, faster boot, better cache hit rates."))
|
(p :class "text-green-800" "Per-page component bundles instead of sending every definition to every page. Smaller payloads, faster boot, better cache hit rates."))
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
(~doc-subsection :title "Implementation"
|
(~doc-subsection :title "Implementation"
|
||||||
|
|
||||||
(p "The dependency analysis algorithm is defined in "
|
(p "The dependency analysis algorithm is defined in "
|
||||||
(a :href "/(language.(spec.deps))" :class "text-violet-700 underline" "deps.sx")
|
(a :href "/sx/(language.(spec.deps))" :class "text-violet-700 underline" "deps.sx")
|
||||||
" — a spec module bootstrapped to every host. Each host loads it via " (code "--spec-modules deps") " and provides 6 platform functions. The spec is the single source of truth; hosts are interchangeable.")
|
" — a spec module bootstrapped to every host. Each host loads it via " (code "--spec-modules deps") " and provides 6 platform functions. The spec is the single source of truth; hosts are interchangeable.")
|
||||||
|
|
||||||
(div :class "space-y-4"
|
(div :class "space-y-4"
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
(li "15 dedicated tests: scan, transitive closure, circular deps, compute-all, components-needed")
|
(li "15 dedicated tests: scan, transitive closure, circular deps, compute-all, components-needed")
|
||||||
(li "Bootstrapped output verified on both host targets")
|
(li "Bootstrapped output verified on both host targets")
|
||||||
(li "Full test suite passes with zero regressions")
|
(li "Full test suite passes with zero regressions")
|
||||||
(li (a :href "/(geography.(isomorphism.bundle-analyzer))" :class "text-violet-700 underline" "Live bundle analyzer") " shows real per-page savings on this app"))))
|
(li (a :href "/sx/(geography.(isomorphism.bundle-analyzer))" :class "text-violet-700 underline" "Live bundle analyzer") " shows real per-page savings on this app"))))
|
||||||
|
|
||||||
;; -----------------------------------------------------------------------
|
;; -----------------------------------------------------------------------
|
||||||
;; Phase 2
|
;; Phase 2
|
||||||
@@ -96,14 +96,14 @@
|
|||||||
(div :class "rounded border border-green-300 bg-green-50 p-4 mb-4"
|
(div :class "rounded border border-green-300 bg-green-50 p-4 mb-4"
|
||||||
(div :class "flex items-center gap-2 mb-2"
|
(div :class "flex items-center gap-2 mb-2"
|
||||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||||
(a :href "/(language.(spec.deps))" :class "text-green-700 underline text-sm font-medium" "View canonical spec: deps.sx")
|
(a :href "/sx/(language.(spec.deps))" :class "text-green-700 underline text-sm font-medium" "View canonical spec: deps.sx")
|
||||||
(a :href "/(geography.(isomorphism.bundle-analyzer))" :class "text-green-700 underline text-sm font-medium" "Live bundle analyzer with IO"))
|
(a :href "/sx/(geography.(isomorphism.bundle-analyzer))" :class "text-green-700 underline text-sm font-medium" "Live bundle analyzer with IO"))
|
||||||
(p :class "text-green-900 font-medium" "What it enables")
|
(p :class "text-green-900 font-medium" "What it enables")
|
||||||
(p :class "text-green-800" "Automatic IO detection and selective expansion. Server expands IO-dependent components, serializes pure ones for client. Per-component intelligence replaces global toggle."))
|
(p :class "text-green-800" "Automatic IO detection and selective expansion. Server expands IO-dependent components, serializes pure ones for client. Per-component intelligence replaces global toggle."))
|
||||||
|
|
||||||
(~doc-subsection :title "IO Detection in the Spec"
|
(~doc-subsection :title "IO Detection in the Spec"
|
||||||
(p "Five new functions in "
|
(p "Five new functions in "
|
||||||
(a :href "/(language.(spec.deps))" :class "text-violet-700 underline" "deps.sx")
|
(a :href "/sx/(language.(spec.deps))" :class "text-violet-700 underline" "deps.sx")
|
||||||
" extend the Phase 1 walker to detect IO primitive references:")
|
" extend the Phase 1 walker to detect IO primitive references:")
|
||||||
|
|
||||||
(div :class "space-y-4"
|
(div :class "space-y-4"
|
||||||
@@ -145,7 +145,7 @@
|
|||||||
(li "Pure components (HTML-only) classified pure with empty io_refs")
|
(li "Pure components (HTML-only) classified pure with empty io_refs")
|
||||||
(li "Transitive IO detection: component calling ~other where ~other calls (current-user) → IO-dependent")
|
(li "Transitive IO detection: component calling ~other where ~other calls (current-user) → IO-dependent")
|
||||||
(li "Bootstrapped to both hosts (sx_ref.py + sx-ref.js)")
|
(li "Bootstrapped to both hosts (sx_ref.py + sx-ref.js)")
|
||||||
(li (a :href "/(geography.(isomorphism.bundle-analyzer))" :class "text-violet-700 underline" "Live bundle analyzer") " shows per-page IO classification"))))
|
(li (a :href "/sx/(geography.(isomorphism.bundle-analyzer))" :class "text-violet-700 underline" "Live bundle analyzer") " shows per-page IO classification"))))
|
||||||
|
|
||||||
;; -----------------------------------------------------------------------
|
;; -----------------------------------------------------------------------
|
||||||
;; Phase 3
|
;; Phase 3
|
||||||
@@ -156,8 +156,8 @@
|
|||||||
(div :class "rounded border border-green-300 bg-green-50 p-4 mb-4"
|
(div :class "rounded border border-green-300 bg-green-50 p-4 mb-4"
|
||||||
(div :class "flex items-center gap-2 mb-2"
|
(div :class "flex items-center gap-2 mb-2"
|
||||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||||
(a :href "/(language.(spec.router))" :class "text-green-700 underline text-sm font-medium" "View canonical spec: router.sx")
|
(a :href "/sx/(language.(spec.router))" :class "text-green-700 underline text-sm font-medium" "View canonical spec: router.sx")
|
||||||
(a :href "/(geography.(isomorphism.routing-analyzer))" :class "text-green-700 underline text-sm font-medium" "Live routing analyzer"))
|
(a :href "/sx/(geography.(isomorphism.routing-analyzer))" :class "text-green-700 underline text-sm font-medium" "Live routing analyzer"))
|
||||||
(p :class "text-green-900 font-medium" "What it enables")
|
(p :class "text-green-900 font-medium" "What it enables")
|
||||||
(p :class "text-green-800" "After initial page load, pure pages render instantly without server roundtrips. Client matches routes locally, evaluates content expressions with cached components, and only falls back to server for pages with :data dependencies."))
|
(p :class "text-green-800" "After initial page load, pure pages render instantly without server roundtrips. Client matches routes locally, evaluates content expressions with cached components, and only falls back to server for pages with :data dependencies."))
|
||||||
|
|
||||||
@@ -200,7 +200,7 @@
|
|||||||
(li (code "/reference/<slug>") " (has " (code ":data (reference-data slug)") ")")
|
(li (code "/reference/<slug>") " (has " (code ":data (reference-data slug)") ")")
|
||||||
(li (code "/language/bootstrappers/<slug>") " (has " (code ":data (bootstrapper-data slug)") ")")
|
(li (code "/language/bootstrappers/<slug>") " (has " (code ":data (bootstrapper-data slug)") ")")
|
||||||
(li (code "/geography/isomorphism/bundle-analyzer") " (has " (code ":data (bundle-analyzer-data)") ")")
|
(li (code "/geography/isomorphism/bundle-analyzer") " (has " (code ":data (bundle-analyzer-data)") ")")
|
||||||
(li (code "/geography/isomorphism/data-test") " (has " (code ":data (data-test-data)") " — " (a :href "/(geography.(isomorphism.data-test))" :class "text-violet-700 underline" "Phase 4 demo") ")")))
|
(li (code "/geography/isomorphism/data-test") " (has " (code ":data (data-test-data)") " — " (a :href "/sx/(geography.(isomorphism.data-test))" :class "text-violet-700 underline" "Phase 4 demo") ")")))
|
||||||
|
|
||||||
(~doc-subsection :title "Try-first/fallback design"
|
(~doc-subsection :title "Try-first/fallback design"
|
||||||
(p "Client routing uses a try-first approach: attempt local evaluation in a try/catch, fall back to server fetch on any failure. This avoids needing perfect static analysis of content expressions — if a content expression calls a page helper the client doesn't have, the eval throws, and the server handles it transparently.")
|
(p "Client routing uses a try-first approach: attempt local evaluation in a try/catch, fall back to server fetch on any failure. This avoids needing perfect static analysis of content expressions — if a content expression calls a page helper the client doesn't have, the eval throws, and the server handles it transparently.")
|
||||||
@@ -232,7 +232,7 @@
|
|||||||
(div :class "rounded border border-green-300 bg-green-50 p-4 mb-4"
|
(div :class "rounded border border-green-300 bg-green-50 p-4 mb-4"
|
||||||
(div :class "flex items-center gap-2 mb-2"
|
(div :class "flex items-center gap-2 mb-2"
|
||||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||||
(a :href "/(geography.(isomorphism.data-test))" :class "text-green-700 underline text-sm font-medium" "Live data test page"))
|
(a :href "/sx/(geography.(isomorphism.data-test))" :class "text-green-700 underline text-sm font-medium" "Live data test page"))
|
||||||
(p :class "text-green-900 font-medium" "What it enables")
|
(p :class "text-green-900 font-medium" "What it enables")
|
||||||
(p :class "text-green-800" "Client fetches server-evaluated data and renders :data pages locally. Data cached with TTL to avoid redundant fetches on back/forward navigation. All IO stays server-side — no continuations needed."))
|
(p :class "text-green-800" "Client fetches server-evaluated data and renders :data pages locally. Data cached with TTL to avoid redundant fetches on back/forward navigation. All IO stays server-side — no continuations needed."))
|
||||||
|
|
||||||
@@ -258,7 +258,7 @@
|
|||||||
(li "Cache miss: " (code "sx:route client+data /path") " — fetches from server, caches, renders")
|
(li "Cache miss: " (code "sx:route client+data /path") " — fetches from server, caches, renders")
|
||||||
(li "Cache hit: " (code "sx:route client+cache /path") " — instant render from cached data")
|
(li "Cache hit: " (code "sx:route client+cache /path") " — instant render from cached data")
|
||||||
(li "After TTL: stale entry evicted, fresh fetch on next visit"))
|
(li "After TTL: stale entry evicted, fresh fetch on next visit"))
|
||||||
(p "Try it: navigate to the " (a :href "/(geography.(isomorphism.data-test))" :class "text-violet-700 underline" "data test page") ", go back, return within 30s — the server-time stays the same (cached). Wait 30s+ and return — new time (fresh fetch)."))))
|
(p "Try it: navigate to the " (a :href "/sx/(geography.(isomorphism.data-test))" :class "text-violet-700 underline" "data test page") ", go back, return within 30s — the server-time stays the same (cached). Wait 30s+ and return — new time (fresh fetch)."))))
|
||||||
|
|
||||||
(~doc-subsection :title "Files"
|
(~doc-subsection :title "Files"
|
||||||
(ul :class "list-disc pl-5 text-stone-700 space-y-1 font-mono text-sm"
|
(ul :class "list-disc pl-5 text-stone-700 space-y-1 font-mono text-sm"
|
||||||
@@ -273,7 +273,7 @@
|
|||||||
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
||||||
(li "30 unit tests: serialize roundtrip, kebab-case, deps, full pipeline simulation, cache TTL")
|
(li "30 unit tests: serialize roundtrip, kebab-case, deps, full pipeline simulation, cache TTL")
|
||||||
(li "Console: " (code "sx:route client+data") " on first visit, " (code "sx:route client+cache") " on return within 30s")
|
(li "Console: " (code "sx:route client+data") " on first visit, " (code "sx:route client+cache") " on return within 30s")
|
||||||
(li (a :href "/(geography.(isomorphism.data-test))" :class "text-violet-700 underline" "Live data test page") " exercises the full pipeline with server time + pipeline steps")
|
(li (a :href "/sx/(geography.(isomorphism.data-test))" :class "text-violet-700 underline" "Live data test page") " exercises the full pipeline with server time + pipeline steps")
|
||||||
(li "append! and dict-set! registered as proper primitives in spec + both hosts"))))
|
(li "append! and dict-set! registered as proper primitives in spec + both hosts"))))
|
||||||
|
|
||||||
;; -----------------------------------------------------------------------
|
;; -----------------------------------------------------------------------
|
||||||
@@ -331,7 +331,7 @@
|
|||||||
(div :class "rounded border border-green-300 bg-green-50 p-4 mb-4"
|
(div :class "rounded border border-green-300 bg-green-50 p-4 mb-4"
|
||||||
(div :class "flex items-center gap-2 mb-2"
|
(div :class "flex items-center gap-2 mb-2"
|
||||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||||
(a :href "/(geography.(isomorphism.streaming))" :class "text-green-700 underline text-sm font-medium" "Live streaming demo"))
|
(a :href "/sx/(geography.(isomorphism.streaming))" :class "text-green-700 underline text-sm font-medium" "Live streaming demo"))
|
||||||
(p :class "text-green-900 font-medium" "What it enables")
|
(p :class "text-green-900 font-medium" "What it enables")
|
||||||
(p :class "text-green-800" "Server streams partially-evaluated SX as IO resolves. Client renders available subtrees immediately with loading skeletons, fills in suspended parts as data arrives."))
|
(p :class "text-green-800" "Server streams partially-evaluated SX as IO resolves. Client renders available subtrees immediately with loading skeletons, fills in suspended parts as data arrives."))
|
||||||
|
|
||||||
@@ -392,9 +392,9 @@
|
|||||||
(li "sx/sxc/pages/helpers.py — streaming-demo-data page helper")))
|
(li "sx/sxc/pages/helpers.py — streaming-demo-data page helper")))
|
||||||
|
|
||||||
(~doc-subsection :title "Demonstration"
|
(~doc-subsection :title "Demonstration"
|
||||||
(p "The " (a :href "/(geography.(isomorphism.streaming))" :class "text-violet-700 underline" "streaming demo page") " exercises the full pipeline:")
|
(p "The " (a :href "/sx/(geography.(isomorphism.streaming))" :class "text-violet-700 underline" "streaming demo page") " exercises the full pipeline:")
|
||||||
(ol :class "list-decimal pl-5 text-stone-700 space-y-1"
|
(ol :class "list-decimal pl-5 text-stone-700 space-y-1"
|
||||||
(li "Navigate to " (a :href "/(geography.(isomorphism.streaming))" :class "text-violet-700 underline" "/(geography.(isomorphism.streaming))"))
|
(li "Navigate to " (a :href "/sx/(geography.(isomorphism.streaming))" :class "text-violet-700 underline" "/sx/(geography.(isomorphism.streaming))"))
|
||||||
(li "The page skeleton appears " (strong "instantly") " — animated loading skeletons fill the content area")
|
(li "The page skeleton appears " (strong "instantly") " — animated loading skeletons fill the content area")
|
||||||
(li "After ~1.5 seconds, the real content replaces the skeletons (streamed from server)")
|
(li "After ~1.5 seconds, the real content replaces the skeletons (streamed from server)")
|
||||||
(li "Open the Network tab — observe " (code "Transfer-Encoding: chunked") " on the document response")
|
(li "Open the Network tab — observe " (code "Transfer-Encoding: chunked") " on the document response")
|
||||||
@@ -481,7 +481,7 @@
|
|||||||
(~doc-subsection :title "Verification"
|
(~doc-subsection :title "Verification"
|
||||||
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
||||||
(li "5 new spec tests (page-render-plan suite)")
|
(li "5 new spec tests (page-render-plan suite)")
|
||||||
(li "Render plans visible on " (a :href "/(geography.(isomorphism.affinity))" "affinity demo page"))
|
(li "Render plans visible on " (a :href "/sx/(geography.(isomorphism.affinity))" "affinity demo page"))
|
||||||
(li "Client page registry includes :render-plan for each page"))))
|
(li "Client page registry includes :render-plan for each page"))))
|
||||||
|
|
||||||
(~doc-subsection :title "7c. Cache Invalidation & Optimistic Data Updates"
|
(~doc-subsection :title "7c. Cache Invalidation & Optimistic Data Updates"
|
||||||
@@ -518,7 +518,7 @@
|
|||||||
|
|
||||||
(~doc-subsection :title "Verification"
|
(~doc-subsection :title "Verification"
|
||||||
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
||||||
(li "Live demo at " (a :href "/(geography.(isomorphism.optimistic))" :class "text-violet-600 hover:underline" "/(geography.(isomorphism.optimistic))"))
|
(li "Live demo at " (a :href "/sx/(geography.(isomorphism.optimistic))" :class "text-violet-600 hover:underline" "/sx/(geography.(isomorphism.optimistic))"))
|
||||||
(li "Console log: " (code "sx:optimistic confirmed") " / " (code "sx:optimistic reverted")))))
|
(li "Console log: " (code "sx:optimistic confirmed") " / " (code "sx:optimistic reverted")))))
|
||||||
|
|
||||||
(~doc-subsection :title "7d. Offline Data Layer"
|
(~doc-subsection :title "7d. Offline Data Layer"
|
||||||
@@ -553,7 +553,7 @@
|
|||||||
|
|
||||||
(~doc-subsection :title "Verification"
|
(~doc-subsection :title "Verification"
|
||||||
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
||||||
(li "Live demo at " (a :href "/(geography.(isomorphism.offline))" :class "text-violet-600 hover:underline" "/(geography.(isomorphism.offline))"))
|
(li "Live demo at " (a :href "/sx/(geography.(isomorphism.offline))" :class "text-violet-600 hover:underline" "/sx/(geography.(isomorphism.offline))"))
|
||||||
(li "Test with DevTools Network → Offline mode")
|
(li "Test with DevTools Network → Offline mode")
|
||||||
(li "Console log: " (code "sx:offline queued") ", " (code "sx:offline syncing") ", " (code "sx:offline synced")))))
|
(li "Console log: " (code "sx:offline queued") ", " (code "sx:offline syncing") ", " (code "sx:offline synced")))))
|
||||||
|
|
||||||
|
|||||||
@@ -479,7 +479,7 @@ python test_js_compile.py # renders both, diffs DOM" "bash")))
|
|||||||
(code "createElement") ", " (code "appendChild") ", " (code "textContent")
|
(code "createElement") ", " (code "appendChild") ", " (code "textContent")
|
||||||
". This gives SX a compilation target competitive with Svelte's "
|
". This gives SX a compilation target competitive with Svelte's "
|
||||||
"approach: components compile away, the framework disappears.")
|
"approach: components compile away, the framework disappears.")
|
||||||
(p "Combined with the " (a :href "/(etc.(plan.content-addressed-components))" "content-addressed components")
|
(p "Combined with the " (a :href "/sx/(etc.(plan.content-addressed-components))" "content-addressed components")
|
||||||
" plan, a page's compiled JS could be stored on IPFS by its content hash. "
|
" plan, a page's compiled JS could be stored on IPFS by its content hash. "
|
||||||
"The server returns a CID. The browser fetches and executes pre-compiled JavaScript. "
|
"The server returns a CID. The browser fetches and executes pre-compiled JavaScript. "
|
||||||
"No parser, no evaluator, no network round-trip for component definitions."))
|
"No parser, no evaluator, no network round-trip for component definitions."))
|
||||||
|
|||||||
@@ -248,7 +248,7 @@
|
|||||||
|
|
||||||
(~doc-section :title "Relationship to Isomorphic Roadmap" :id "relationship"
|
(~doc-section :title "Relationship to Isomorphic Roadmap" :id "relationship"
|
||||||
(p "This plan sits between Phase 3 (client-side routing) and Phase 4 (client async & IO bridge) of the "
|
(p "This plan sits between Phase 3 (client-side routing) and Phase 4 (client async & IO bridge) of the "
|
||||||
(a :href "/(etc.(plan.isomorphic-architecture))" :class "text-violet-700 underline" "isomorphic architecture roadmap")
|
(a :href "/sx/(etc.(plan.isomorphic-architecture))" :class "text-violet-700 underline" "isomorphic architecture roadmap")
|
||||||
". It extends Phase 3 by making more navigations go client-side without needing any IO bridge — purely by ensuring component definitions are available before they're needed.")
|
". It extends Phase 3 by making more navigations go client-side without needing any IO bridge — purely by ensuring component definitions are available before they're needed.")
|
||||||
(div :class "rounded border border-amber-200 bg-amber-50 p-3 mt-2"
|
(div :class "rounded border border-amber-200 bg-amber-50 p-3 mt-2"
|
||||||
(p :class "text-amber-800 text-sm" (strong "Depends on: ") "Phase 3 (client-side routing with deps checking). No dependency on Phase 4.")))))
|
(p :class "text-amber-800 text-sm" (strong "Depends on: ") "Phase 3 (client-side routing with deps checking). No dependency on Phase 4.")))))
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
(~doc-code :code (highlight "#'my-function → (quote my-function)" "lisp")))
|
(~doc-code :code (highlight "#'my-function → (quote my-function)" "lisp")))
|
||||||
|
|
||||||
(~doc-subsection :title "Extensible dispatch: #name"
|
(~doc-subsection :title "Extensible dispatch: #name"
|
||||||
(p "User-defined reader macros via " (code "#name expr") ". The parser reads an identifier after " (code "#") ", looks up a handler in the reader macro registry, and calls it with the next parsed expression. See the " (a :href "/(etc.(plan.reader-macro-demo))" :class "text-violet-600 hover:underline" "#z3 demo") " for a working example that translates SX spec declarations to SMT-LIB.")))
|
(p "User-defined reader macros via " (code "#name expr") ". The parser reads an identifier after " (code "#") ", looks up a handler in the reader macro registry, and calls it with the next parsed expression. See the " (a :href "/sx/(etc.(plan.reader-macro-demo))" :class "text-violet-600 hover:underline" "#z3 demo") " for a working example that translates SX spec declarations to SMT-LIB.")))
|
||||||
|
|
||||||
|
|
||||||
;; -----------------------------------------------------------------------
|
;; -----------------------------------------------------------------------
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
;; -----------------------------------------------------------------------
|
;; -----------------------------------------------------------------------
|
||||||
|
|
||||||
(~doc-section :title "Tiers" :id "tiers"
|
(~doc-section :title "Tiers" :id "tiers"
|
||||||
(p "Four tiers, matching the " (a :href "/(geography.(reactive.plan))" :class "text-violet-700 underline" "reactive islands") " levels:")
|
(p "Four tiers, matching the " (a :href "/sx/(geography.(reactive.plan))" :class "text-violet-700 underline" "reactive islands") " levels:")
|
||||||
|
|
||||||
(div :class "overflow-x-auto rounded border border-stone-200 mb-4"
|
(div :class "overflow-x-auto rounded border border-stone-200 mb-4"
|
||||||
(table :class "w-full text-left text-sm"
|
(table :class "w-full text-left text-sm"
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
;; -----------------------------------------------------------------------
|
;; -----------------------------------------------------------------------
|
||||||
|
|
||||||
(~doc-section :title "The Slicer is SX" :id "slicer-is-sx"
|
(~doc-section :title "The Slicer is SX" :id "slicer-is-sx"
|
||||||
(p "Per the " (a :href "/(etc.(plan.self-hosting-bootstrapper))" :class "text-violet-700 underline" "self-hosting principle") ", the slicer is not a build tool — it's a spec module. " (code "slice.sx") " analyzes the spec's own dependency graph and determines which defines belong to which tier.")
|
(p "Per the " (a :href "/sx/(etc.(plan.self-hosting-bootstrapper))" :class "text-violet-700 underline" "self-hosting principle") ", the slicer is not a build tool — it's a spec module. " (code "slice.sx") " analyzes the spec's own dependency graph and determines which defines belong to which tier.")
|
||||||
(p (code "js.sx") " (the self-hosting SX-to-JavaScript translator) already compiles the full spec. Slicing is a filter: " (code "js.sx") " translates only the defines that " (code "slice.sx") " selects for a given tier.")
|
(p (code "js.sx") " (the self-hosting SX-to-JavaScript translator) already compiles the full spec. Slicing is a filter: " (code "js.sx") " translates only the defines that " (code "slice.sx") " selects for a given tier.")
|
||||||
|
|
||||||
(~doc-code :code (highlight ";; slice.sx — determine which defines each tier needs\n;;\n;; Input: the full list of defines from all spec files\n;; Output: a filtered list for the requested tier\n\n(define tier-deps\n ;; Which spec modules each tier requires\n {:L0 (list \"engine\" \"boot-partial\")\n :L1 (list \"engine\" \"boot-partial\" \"dom-partial\")\n :L2 (list \"engine\" \"boot-partial\" \"dom-partial\"\n \"signals\" \"dom-island\")\n :L3 (list \"eval\" \"render\" \"parser\"\n \"engine\" \"orchestration\" \"boot\"\n \"dom\" \"signals\" \"router\")})\n\n(define slice-defines\n (fn (tier all-defines)\n ;; 1. Get the module list for this tier\n ;; 2. Walk each define's dependency references\n ;; 3. Include a define only if ALL its deps are\n ;; satisfiable within the tier's module set\n ;; 4. Return the filtered define list\n (let ((modules (get tier-deps tier)))\n (filter\n (fn (d) (tier-satisfies? modules (define-deps d)))\n all-defines))))" "lisp"))
|
(~doc-code :code (highlight ";; slice.sx — determine which defines each tier needs\n;;\n;; Input: the full list of defines from all spec files\n;; Output: a filtered list for the requested tier\n\n(define tier-deps\n ;; Which spec modules each tier requires\n {:L0 (list \"engine\" \"boot-partial\")\n :L1 (list \"engine\" \"boot-partial\" \"dom-partial\")\n :L2 (list \"engine\" \"boot-partial\" \"dom-partial\"\n \"signals\" \"dom-island\")\n :L3 (list \"eval\" \"render\" \"parser\"\n \"engine\" \"orchestration\" \"boot\"\n \"dom\" \"signals\" \"router\")})\n\n(define slice-defines\n (fn (tier all-defines)\n ;; 1. Get the module list for this tier\n ;; 2. Walk each define's dependency references\n ;; 3. Include a define only if ALL its deps are\n ;; satisfiable within the tier's module set\n ;; 4. Return the filtered define list\n (let ((modules (get tier-deps tier)))\n (filter\n (fn (d) (tier-satisfies? modules (define-deps d)))\n all-defines))))" "lisp"))
|
||||||
@@ -161,7 +161,7 @@
|
|||||||
|
|
||||||
(~doc-subsection :title "Cache Behavior"
|
(~doc-subsection :title "Cache Behavior"
|
||||||
(p "Each tier file is content-hashed (like the current " (code "sx_js_hash") " mechanism). Cache-forever semantics. A user who visits any L0 page caches the L0 runtime permanently. If they later visit an L2 page, only the ~10KB delta downloads.")
|
(p "Each tier file is content-hashed (like the current " (code "sx_js_hash") " mechanism). Cache-forever semantics. A user who visits any L0 page caches the L0 runtime permanently. If they later visit an L2 page, only the ~10KB delta downloads.")
|
||||||
(p "Combined with " (a :href "/(etc.(plan.environment-images))" :class "text-violet-700 underline" "environment images") ": the image CID includes the tier. An L0 image is smaller than an L3 image — it contains fewer primitives, no parser state, no evaluator. The standalone HTML bundle for an L0 page is tiny.")))
|
(p "Combined with " (a :href "/sx/(etc.(plan.environment-images))" :class "text-violet-700 underline" "environment images") ": the image CID includes the tier. An L0 image is smaller than an L3 image — it contains fewer primitives, no parser state, no evaluator. The standalone HTML bundle for an L0 page is tiny.")))
|
||||||
|
|
||||||
;; -----------------------------------------------------------------------
|
;; -----------------------------------------------------------------------
|
||||||
;; Automatic tier detection
|
;; Automatic tier detection
|
||||||
@@ -273,10 +273,10 @@
|
|||||||
|
|
||||||
(~doc-section :title "Relationships" :id "relationships"
|
(~doc-section :title "Relationships" :id "relationships"
|
||||||
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
||||||
(li (a :href "/(etc.(plan.environment-images))" :class "text-violet-700 underline" "Environment Images") " — tiered images are smaller. An L0 image omits the parser, evaluator, and most primitives.")
|
(li (a :href "/sx/(etc.(plan.environment-images))" :class "text-violet-700 underline" "Environment Images") " — tiered images are smaller. An L0 image omits the parser, evaluator, and most primitives.")
|
||||||
(li (a :href "/(etc.(plan.content-addressed-components))" :class "text-violet-700 underline" "Content-Addressed Components") " — component CID resolution is L3-only. L0 pages don't resolve components client-side.")
|
(li (a :href "/sx/(etc.(plan.content-addressed-components))" :class "text-violet-700 underline" "Content-Addressed Components") " — component CID resolution is L3-only. L0 pages don't resolve components client-side.")
|
||||||
(li (a :href "/(geography.(reactive.plan))" :class "text-violet-700 underline" "Reactive Islands") " — L2 tier is defined by island presence. The signal runtime is the L1→L2 delta.")
|
(li (a :href "/sx/(geography.(reactive.plan))" :class "text-violet-700 underline" "Reactive Islands") " — L2 tier is defined by island presence. The signal runtime is the L1→L2 delta.")
|
||||||
(li (a :href "/(etc.(plan.isomorphic-architecture))" :class "text-violet-700 underline" "Isomorphic Architecture") " — client-side page rendering is L3. Most pages don't need it."))
|
(li (a :href "/sx/(etc.(plan.isomorphic-architecture))" :class "text-violet-700 underline" "Isomorphic Architecture") " — client-side page rendering is L3. Most pages don't need it."))
|
||||||
|
|
||||||
(div :class "rounded border border-amber-200 bg-amber-50 p-3 mt-2"
|
(div :class "rounded border border-amber-200 bg-amber-50 p-3 mt-2"
|
||||||
(p :class "text-amber-800 text-sm" (strong "Depends on: ") (code "js.sx") " (complete), " (code "deps.sx") " (complete), " (code "bootstrap_js.py") " adapter selection (exists). " (strong "New: ") (code "slice.sx") " spec module.")))))
|
(p :class "text-amber-800 text-sm" (strong "Depends on: ") (code "js.sx") " (complete), " (code "deps.sx") " (complete), " (code "bootstrap_js.py") " adapter selection (exists). " (strong "New: ") (code "slice.sx") " spec module.")))))
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
(p :class "text-green-700 text-sm"
|
(p :class "text-green-700 text-sm"
|
||||||
(code "py.sx") " is implemented and verified. G0 == G1: 128/128 defines match, "
|
(code "py.sx") " is implemented and verified. G0 == G1: 128/128 defines match, "
|
||||||
"1490 lines, 88,955 bytes — byte-for-byte identical. "
|
"1490 lines, 88,955 bytes — byte-for-byte identical. "
|
||||||
(a :href "/(language.(bootstrapper.self-hosting))" :class "underline text-green-600 font-medium"
|
(a :href "/sx/(language.(bootstrapper.self-hosting))" :class "underline text-green-600 font-medium"
|
||||||
"See live verification."))))
|
"See live verification."))))
|
||||||
|
|
||||||
;; -----------------------------------------------------------------------
|
;; -----------------------------------------------------------------------
|
||||||
|
|||||||
@@ -37,37 +37,37 @@
|
|||||||
(div :class "rounded border border-green-200 bg-green-50 p-4"
|
(div :class "rounded border border-green-200 bg-green-50 p-4"
|
||||||
(div :class "flex items-center gap-2 mb-1"
|
(div :class "flex items-center gap-2 mb-1"
|
||||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||||
(a :href "/(geography.(isomorphism))" :class "font-semibold text-green-800 underline" "Isomorphic Phase 1: Dependency Analysis"))
|
(a :href "/sx/(geography.(isomorphism))" :class "font-semibold text-green-800 underline" "Isomorphic Phase 1: Dependency Analysis"))
|
||||||
(p :class "text-sm text-stone-600" "Per-page component bundles via deps.sx. Transitive closure, scan-refs, components-needed, page-css-classes. 15 tests, bootstrapped to both hosts."))
|
(p :class "text-sm text-stone-600" "Per-page component bundles via deps.sx. Transitive closure, scan-refs, components-needed, page-css-classes. 15 tests, bootstrapped to both hosts."))
|
||||||
|
|
||||||
(div :class "rounded border border-green-200 bg-green-50 p-4"
|
(div :class "rounded border border-green-200 bg-green-50 p-4"
|
||||||
(div :class "flex items-center gap-2 mb-1"
|
(div :class "flex items-center gap-2 mb-1"
|
||||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||||
(a :href "/(geography.(isomorphism))" :class "font-semibold text-green-800 underline" "Isomorphic Phase 2: IO Detection"))
|
(a :href "/sx/(geography.(isomorphism))" :class "font-semibold text-green-800 underline" "Isomorphic Phase 2: IO Detection"))
|
||||||
(p :class "text-sm text-stone-600" "Automatic IO classification. scan-io-refs, transitive-io-refs, compute-all-io-refs. Server expands IO components, serializes pure ones for client."))
|
(p :class "text-sm text-stone-600" "Automatic IO classification. scan-io-refs, transitive-io-refs, compute-all-io-refs. Server expands IO components, serializes pure ones for client."))
|
||||||
|
|
||||||
(div :class "rounded border border-green-200 bg-green-50 p-4"
|
(div :class "rounded border border-green-200 bg-green-50 p-4"
|
||||||
(div :class "flex items-center gap-2 mb-1"
|
(div :class "flex items-center gap-2 mb-1"
|
||||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||||
(a :href "/(geography.(isomorphism))" :class "font-semibold text-green-800 underline" "Isomorphic Phase 3: Client-Side Routing"))
|
(a :href "/sx/(geography.(isomorphism))" :class "font-semibold text-green-800 underline" "Isomorphic Phase 3: Client-Side Routing"))
|
||||||
(p :class "text-sm text-stone-600" "router.sx spec, page registry via <script type=\"text/sx-pages\">, client route matching, try-first/fallback to server. Pure pages render without server roundtrips."))
|
(p :class "text-sm text-stone-600" "router.sx spec, page registry via <script type=\"text/sx-pages\">, client route matching, try-first/fallback to server. Pure pages render without server roundtrips."))
|
||||||
|
|
||||||
(div :class "rounded border border-green-200 bg-green-50 p-4"
|
(div :class "rounded border border-green-200 bg-green-50 p-4"
|
||||||
(div :class "flex items-center gap-2 mb-1"
|
(div :class "flex items-center gap-2 mb-1"
|
||||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||||
(a :href "/(geography.(isomorphism))" :class "font-semibold text-green-800 underline" "Isomorphic Phase 4: Client Async & IO Bridge"))
|
(a :href "/sx/(geography.(isomorphism))" :class "font-semibold text-green-800 underline" "Isomorphic Phase 4: Client Async & IO Bridge"))
|
||||||
(p :class "text-sm text-stone-600" "Server evaluates :data expressions, serializes as SX wire format. Client fetches pre-evaluated data, caches with 30s TTL, renders :content locally. 30 unit tests."))
|
(p :class "text-sm text-stone-600" "Server evaluates :data expressions, serializes as SX wire format. Client fetches pre-evaluated data, caches with 30s TTL, renders :content locally. 30 unit tests."))
|
||||||
|
|
||||||
(div :class "rounded border border-green-200 bg-green-50 p-4"
|
(div :class "rounded border border-green-200 bg-green-50 p-4"
|
||||||
(div :class "flex items-center gap-2 mb-1"
|
(div :class "flex items-center gap-2 mb-1"
|
||||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||||
(a :href "/(geography.(isomorphism))" :class "font-semibold text-green-800 underline" "Isomorphic Phase 5: Client IO Proxy"))
|
(a :href "/sx/(geography.(isomorphism))" :class "font-semibold text-green-800 underline" "Isomorphic Phase 5: Client IO Proxy"))
|
||||||
(p :class "text-sm text-stone-600" "IO primitives (highlight, asset-url, etc.) proxied to server via registerIoDeps(). Async DOM renderer handles promises through the render tree. Components with IO deps render client-side via server round-trips — no continuations needed."))
|
(p :class "text-sm text-stone-600" "IO primitives (highlight, asset-url, etc.) proxied to server via registerIoDeps(). Async DOM renderer handles promises through the render tree. Components with IO deps render client-side via server round-trips — no continuations needed."))
|
||||||
|
|
||||||
(div :class "rounded border border-green-200 bg-green-50 p-4"
|
(div :class "rounded border border-green-200 bg-green-50 p-4"
|
||||||
(div :class "flex items-center gap-2 mb-1"
|
(div :class "flex items-center gap-2 mb-1"
|
||||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||||
(a :href "/(language.(test))" :class "font-semibold text-green-800 underline" "Modular Test Architecture"))
|
(a :href "/sx/(language.(test))" :class "font-semibold text-green-800 underline" "Modular Test Architecture"))
|
||||||
(p :class "text-sm text-stone-600" "Per-module test specs (eval, parser, router, render) with 161 tests. Three runners: Python, Node.js, browser. 5 platform functions, everything else pure SX."))))
|
(p :class "text-sm text-stone-600" "Per-module test specs (eval, parser, router, render) with 161 tests. Three runners: Python, Node.js, browser. 5 platform functions, everything else pure SX."))))
|
||||||
|
|
||||||
;; -----------------------------------------------------------------------
|
;; -----------------------------------------------------------------------
|
||||||
@@ -81,7 +81,7 @@
|
|||||||
(div :class "rounded border border-amber-200 bg-amber-50 p-4"
|
(div :class "rounded border border-amber-200 bg-amber-50 p-4"
|
||||||
(div :class "flex items-center gap-2 mb-1"
|
(div :class "flex items-center gap-2 mb-1"
|
||||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-amber-600 text-white uppercase" "Partial")
|
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-amber-600 text-white uppercase" "Partial")
|
||||||
(a :href "/(etc.(plan.fragment-protocol))" :class "font-semibold text-amber-900 underline" "Fragment Protocol"))
|
(a :href "/sx/(etc.(plan.fragment-protocol))" :class "font-semibold text-amber-900 underline" "Fragment Protocol"))
|
||||||
(p :class "text-sm text-stone-600" "Fragment GET infrastructure works. The planned POST/sexp structured protocol for transferring component definitions between services is not yet implemented. Fragment endpoints still use legacy GET + X-Fragment-Request headers."))))
|
(p :class "text-sm text-stone-600" "Fragment GET infrastructure works. The planned POST/sexp structured protocol for transferring component definitions between services is not yet implemented. Fragment endpoints still use legacy GET + X-Fragment-Request headers."))))
|
||||||
|
|
||||||
;; -----------------------------------------------------------------------
|
;; -----------------------------------------------------------------------
|
||||||
@@ -95,49 +95,49 @@
|
|||||||
(div :class "rounded border border-stone-200 bg-stone-50 p-4"
|
(div :class "rounded border border-stone-200 bg-stone-50 p-4"
|
||||||
(div :class "flex items-center gap-2 mb-1"
|
(div :class "flex items-center gap-2 mb-1"
|
||||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-700 text-white uppercase" "Done")
|
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-700 text-white uppercase" "Done")
|
||||||
(a :href "/(etc.(plan.reader-macros))" :class "font-semibold text-stone-800 underline" "Reader Macros"))
|
(a :href "/sx/(etc.(plan.reader-macros))" :class "font-semibold text-stone-800 underline" "Reader Macros"))
|
||||||
(p :class "text-sm text-stone-600" "# dispatch in parser.sx spec, Python parser.py, hand-written sx.js. Three built-ins (#;, #|...|, #') plus extensible #name dispatch. #z3 demo translates define-primitive to SMT-LIB.")
|
(p :class "text-sm text-stone-600" "# dispatch in parser.sx spec, Python parser.py, hand-written sx.js. Three built-ins (#;, #|...|, #') plus extensible #name dispatch. #z3 demo translates define-primitive to SMT-LIB.")
|
||||||
(p :class "text-sm text-stone-500 mt-1" "48 parser tests (SX + Python), all passing. Rebootstrapped to JS and Python."))
|
(p :class "text-sm text-stone-500 mt-1" "48 parser tests (SX + Python), all passing. Rebootstrapped to JS and Python."))
|
||||||
|
|
||||||
(div :class "rounded border border-stone-200 bg-stone-50 p-4"
|
(div :class "rounded border border-stone-200 bg-stone-50 p-4"
|
||||||
(div :class "flex items-center gap-2 mb-1"
|
(div :class "flex items-center gap-2 mb-1"
|
||||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-stone-500 text-white uppercase" "Not Started")
|
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-stone-500 text-white uppercase" "Not Started")
|
||||||
(a :href "/(etc.(plan.sx-activity))" :class "font-semibold text-stone-800 underline" "SX-Activity"))
|
(a :href "/sx/(etc.(plan.sx-activity))" :class "font-semibold text-stone-800 underline" "SX-Activity"))
|
||||||
(p :class "text-sm text-stone-600" "Federated SX over ActivityPub — 6 phases from SX wire format for activities to the evaluable web on IPFS. Existing AP infrastructure provides the foundation but no SX-specific federation code exists.")
|
(p :class "text-sm text-stone-600" "Federated SX over ActivityPub — 6 phases from SX wire format for activities to the evaluable web on IPFS. Existing AP infrastructure provides the foundation but no SX-specific federation code exists.")
|
||||||
(p :class "text-sm text-stone-500 mt-1" "Remaining: shared/sx/activity.py (SX<->JSON-LD), shared/sx/ipfs.py, shared/sx/ref/ipfs-resolve.sx, shared/sx/registry.py, shared/sx/anchor.py."))
|
(p :class "text-sm text-stone-500 mt-1" "Remaining: shared/sx/activity.py (SX<->JSON-LD), shared/sx/ipfs.py, shared/sx/ref/ipfs-resolve.sx, shared/sx/registry.py, shared/sx/anchor.py."))
|
||||||
|
|
||||||
(div :class "rounded border border-stone-200 bg-stone-50 p-4"
|
(div :class "rounded border border-stone-200 bg-stone-50 p-4"
|
||||||
(div :class "flex items-center gap-2 mb-1"
|
(div :class "flex items-center gap-2 mb-1"
|
||||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-stone-500 text-white uppercase" "Not Started")
|
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-stone-500 text-white uppercase" "Not Started")
|
||||||
(a :href "/(etc.(plan.glue-decoupling))" :class "font-semibold text-stone-800 underline" "Cross-App Decoupling via Glue"))
|
(a :href "/sx/(etc.(plan.glue-decoupling))" :class "font-semibold text-stone-800 underline" "Cross-App Decoupling via Glue"))
|
||||||
(p :class "text-sm text-stone-600" "Eliminate all cross-app model imports by routing through a glue service layer. No glue/ directory exists. Apps are currently decoupled via HTTP interfaces and DTOs instead.")
|
(p :class "text-sm text-stone-600" "Eliminate all cross-app model imports by routing through a glue service layer. No glue/ directory exists. Apps are currently decoupled via HTTP interfaces and DTOs instead.")
|
||||||
(p :class "text-sm text-stone-500 mt-1" "Remaining: glue/services/ for pages, page_config, calendars, marketplaces, cart_items, products, post_associations. 25+ cross-app imports to eliminate."))
|
(p :class "text-sm text-stone-500 mt-1" "Remaining: glue/services/ for pages, page_config, calendars, marketplaces, cart_items, products, post_associations. 25+ cross-app imports to eliminate."))
|
||||||
|
|
||||||
(div :class "rounded border border-stone-200 bg-stone-50 p-4"
|
(div :class "rounded border border-stone-200 bg-stone-50 p-4"
|
||||||
(div :class "flex items-center gap-2 mb-1"
|
(div :class "flex items-center gap-2 mb-1"
|
||||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-stone-500 text-white uppercase" "Not Started")
|
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-stone-500 text-white uppercase" "Not Started")
|
||||||
(a :href "/(etc.(plan.social-sharing))" :class "font-semibold text-stone-800 underline" "Social Network Sharing"))
|
(a :href "/sx/(etc.(plan.social-sharing))" :class "font-semibold text-stone-800 underline" "Social Network Sharing"))
|
||||||
(p :class "text-sm text-stone-600" "OAuth-based sharing to Facebook, Instagram, Threads, Twitter/X, LinkedIn, and Mastodon via the account service. No models, blueprints, or platform clients created.")
|
(p :class "text-sm text-stone-600" "OAuth-based sharing to Facebook, Instagram, Threads, Twitter/X, LinkedIn, and Mastodon via the account service. No models, blueprints, or platform clients created.")
|
||||||
(p :class "text-sm text-stone-500 mt-1" "Remaining: SocialConnection model, social_crypto.py, platform OAuth clients (6), account/bp/social/ blueprint, share button fragment."))
|
(p :class "text-sm text-stone-500 mt-1" "Remaining: SocialConnection model, social_crypto.py, platform OAuth clients (6), account/bp/social/ blueprint, share button fragment."))
|
||||||
|
|
||||||
(div :class "rounded border border-green-200 bg-green-50 p-4"
|
(div :class "rounded border border-green-200 bg-green-50 p-4"
|
||||||
(div :class "flex items-center gap-2 mb-1"
|
(div :class "flex items-center gap-2 mb-1"
|
||||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||||
(a :href "/(geography.(isomorphism))" :class "font-semibold text-stone-800 underline" "Isomorphic Phase 6: Streaming & Suspense"))
|
(a :href "/sx/(geography.(isomorphism))" :class "font-semibold text-stone-800 underline" "Isomorphic Phase 6: Streaming & Suspense"))
|
||||||
(p :class "text-sm text-stone-600" "Server streams partially-evaluated SX as IO resolves. ~suspense component renders fallbacks, inline resolution scripts fill in content. Concurrent IO via asyncio, chunked transfer encoding.")
|
(p :class "text-sm text-stone-600" "Server streams partially-evaluated SX as IO resolves. ~suspense component renders fallbacks, inline resolution scripts fill in content. Concurrent IO via asyncio, chunked transfer encoding.")
|
||||||
(p :class "text-sm text-stone-500 mt-1" "Demo: " (a :href "/(geography.(isomorphism.streaming))" "/(geography.(isomorphism.streaming))")))
|
(p :class "text-sm text-stone-500 mt-1" "Demo: " (a :href "/sx/(geography.(isomorphism.streaming))" "/sx/(geography.(isomorphism.streaming))")))
|
||||||
|
|
||||||
(div :class "rounded border border-green-300 bg-green-50 p-4"
|
(div :class "rounded border border-green-300 bg-green-50 p-4"
|
||||||
(div :class "flex items-center gap-2 mb-1"
|
(div :class "flex items-center gap-2 mb-1"
|
||||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-green-600 text-white uppercase" "Complete")
|
||||||
(a :href "/(geography.(isomorphism))" :class "font-semibold text-stone-800 underline" "Isomorphic Phase 7: Full Isomorphism"))
|
(a :href "/sx/(geography.(isomorphism))" :class "font-semibold text-stone-800 underline" "Isomorphic Phase 7: Full Isomorphism"))
|
||||||
(p :class "text-sm text-stone-600" "Affinity annotations, render plans, optimistic data updates, offline mutation queue, isomorphic testing harness, universal page descriptor.")
|
(p :class "text-sm text-stone-600" "Affinity annotations, render plans, optimistic data updates, offline mutation queue, isomorphic testing harness, universal page descriptor.")
|
||||||
(p :class "text-sm text-stone-500 mt-1" "All 6 sub-phases (7a–7f) complete."))
|
(p :class "text-sm text-stone-500 mt-1" "All 6 sub-phases (7a–7f) complete."))
|
||||||
|
|
||||||
(div :class "rounded border border-stone-200 bg-stone-50 p-4"
|
(div :class "rounded border border-stone-200 bg-stone-50 p-4"
|
||||||
(div :class "flex items-center gap-2 mb-1"
|
(div :class "flex items-center gap-2 mb-1"
|
||||||
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-stone-500 text-white uppercase" "Not Started")
|
(span :class "inline-block px-2 py-0.5 rounded text-xs font-bold bg-stone-500 text-white uppercase" "Not Started")
|
||||||
(a :href "/(etc.(plan.spec-explorer))" :class "font-semibold text-stone-800 underline" "Spec Explorer — The Fifth Ring"))
|
(a :href "/sx/(etc.(plan.spec-explorer))" :class "font-semibold text-stone-800 underline" "Spec Explorer — The Fifth Ring"))
|
||||||
(p :class "text-sm text-stone-600" "SX exploring itself. Per-function cards showing all five rings: SX source (nucleus), Python/JS/Z3 translations (bootstrapper), platform dependencies (bridge), tests and proofs (runtime), and usage examples (application). The documentation is the thing documenting itself.")
|
(p :class "text-sm text-stone-600" "SX exploring itself. Per-function cards showing all five rings: SX source (nucleus), Python/JS/Z3 translations (bootstrapper), platform dependencies (bridge), tests and proofs (runtime), and usage examples (application). The documentation is the thing documenting itself.")
|
||||||
(p :class "text-sm text-stone-500 mt-1" "Prerequisite complete: 180+ functions annotated with :effects across all 14 spec files. Three increments: core + translations, bridge + runtime, examples + polish."))))))
|
(p :class "text-sm text-stone-500 mt-1" "Prerequisite complete: 180+ functions annotated with :effects across all 14 spec files. Three increments: core + translations, bridge + runtime, examples + polish."))))))
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
"Every page is a function, every URL is a function call, and the nav tree hierarchy "
|
"Every page is a function, every URL is a function call, and the nav tree hierarchy "
|
||||||
"is encoded directly in the nesting of the expression.")
|
"is encoded directly in the nesting of the expression.")
|
||||||
(p "Current URLs like " (code "/language/docs/introduction") " become "
|
(p "Current URLs like " (code "/language/docs/introduction") " become "
|
||||||
(code "/(language.(doc.introduction))") ". Dots replace spaces as the URL-friendly "
|
(code "/sx/(language.(doc.introduction))") ". Dots replace spaces as the URL-friendly "
|
||||||
"separator — they are unreserved in RFC 3986, never percent-encoded, and visually clean. "
|
"separator — they are unreserved in RFC 3986, never percent-encoded, and visually clean. "
|
||||||
"The parser treats dot as whitespace: " (code "s/./ /") " before parsing as SX.")
|
"The parser treats dot as whitespace: " (code "s/./ /") " before parsing as SX.")
|
||||||
(~doc-code :code (highlight
|
(~doc-code :code (highlight
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
(~doc-section :title "The Site Is a REPL" :id "repl"
|
(~doc-section :title "The Site Is a REPL" :id "repl"
|
||||||
(p "The address bar becomes the input line of a REPL. The page is the output.")
|
(p "The address bar becomes the input line of a REPL. The page is the output.")
|
||||||
(~doc-code :code (highlight
|
(~doc-code :code (highlight
|
||||||
"/(about) ;; renders the about page\n/(source.(about)) ;; returns the SX source for the about page\n/(eval.(source.(about))) ;; re-evaluates it live\n\n;; The killer demo:\n/(eval.(map.double.(list.1.2.3))) ;; actually returns (2 4 6)\n\n;; The website IS a REPL. The address bar IS the input."
|
"/sx/(about) ;; renders the about page\n/(source.(about)) ;; returns the SX source for the about page\n/(eval.(source.(about))) ;; re-evaluates it live\n\n;; The killer demo:\n/(eval.(map.double.(list.1.2.3))) ;; actually returns (2 4 6)\n\n;; The website IS a REPL. The address bar IS the input."
|
||||||
"lisp"))
|
"lisp"))
|
||||||
(p "You do not need to explain what SX is. You show someone a URL and they "
|
(p "You do not need to explain what SX is. You show someone a URL and they "
|
||||||
"immediately understand the entire language and philosophy. "
|
"immediately understand the entire language and philosophy. "
|
||||||
@@ -144,7 +144,7 @@
|
|||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "py-2 px-3 font-semibold" "Transport")
|
(td :class "py-2 px-3 font-semibold" "Transport")
|
||||||
(td :class "py-2 px-3" "POST JSON (violates HTTP GET semantics)")
|
(td :class "py-2 px-3" "POST JSON (violates HTTP GET semantics)")
|
||||||
(td :class "py-2 px-3" "GET " (code "/(expr)") " — cacheable, bookmarkable"))
|
(td :class "py-2 px-3" "GET " (code "/sx/(expr)") " — cacheable, bookmarkable"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "py-2 px-3 font-semibold" "Response")
|
(td :class "py-2 px-3 font-semibold" "Response")
|
||||||
(td :class "py-2 px-3" "JSON data (needs separate rendering)")
|
(td :class "py-2 px-3" "JSON data (needs separate rendering)")
|
||||||
@@ -185,31 +185,31 @@
|
|||||||
(tbody :class "text-stone-600"
|
(tbody :class "text-stone-600"
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "py-2 px-3 font-mono text-violet-700" "source")
|
(td :class "py-2 px-3 font-mono text-violet-700" "source")
|
||||||
(td :class "py-2 px-3 font-mono text-sm" "/(source.(~essay-sx-sucks))")
|
(td :class "py-2 px-3 font-mono text-sm" "/sx/(source.(~essay-sx-sucks))")
|
||||||
(td :class "py-2 px-3" "Show defcomp source instead of rendering"))
|
(td :class "py-2 px-3" "Show defcomp source instead of rendering"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "py-2 px-3 font-mono text-violet-700" "inspect")
|
(td :class "py-2 px-3 font-mono text-violet-700" "inspect")
|
||||||
(td :class "py-2 px-3 font-mono text-sm" "/(inspect.(language.(doc.primitives)))")
|
(td :class "py-2 px-3 font-mono text-sm" "/sx/(inspect.(language.(doc.primitives)))")
|
||||||
(td :class "py-2 px-3" "Deps, CSS classes, render plan, IO requirements"))
|
(td :class "py-2 px-3" "Deps, CSS classes, render plan, IO requirements"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "py-2 px-3 font-mono text-violet-700" "diff")
|
(td :class "py-2 px-3 font-mono text-violet-700" "diff")
|
||||||
(td :class "py-2 px-3 font-mono text-sm" "/(diff.(spec.signals).(spec.eval))")
|
(td :class "py-2 px-3 font-mono text-sm" "/sx/(diff.(spec.signals).(spec.eval))")
|
||||||
(td :class "py-2 px-3" "Side-by-side two pages"))
|
(td :class "py-2 px-3" "Side-by-side two pages"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "py-2 px-3 font-mono text-violet-700" "search")
|
(td :class "py-2 px-3 font-mono text-violet-700" "search")
|
||||||
(td :class "py-2 px-3 font-mono text-sm" "/(search.\"define\".:in.(spec.signals))")
|
(td :class "py-2 px-3 font-mono text-sm" "/sx/(search.\"define\".:in.(spec.signals))")
|
||||||
(td :class "py-2 px-3" "Grep within a page or spec"))
|
(td :class "py-2 px-3" "Grep within a page or spec"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "py-2 px-3 font-mono text-violet-700" "raw")
|
(td :class "py-2 px-3 font-mono text-violet-700" "raw")
|
||||||
(td :class "py-2 px-3 font-mono text-sm" "/(raw.(~some-component))")
|
(td :class "py-2 px-3 font-mono text-sm" "/sx/(raw.(~some-component))")
|
||||||
(td :class "py-2 px-3" "Skip ~sx-doc nav wrapping"))
|
(td :class "py-2 px-3" "Skip ~sx-doc nav wrapping"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "py-2 px-3 font-mono text-violet-700" "eval")
|
(td :class "py-2 px-3 font-mono text-violet-700" "eval")
|
||||||
(td :class "py-2 px-3 font-mono text-sm" "/(eval.(map.double.(list.1.2.3)))")
|
(td :class "py-2 px-3 font-mono text-sm" "/sx/(eval.(map.double.(list.1.2.3)))")
|
||||||
(td :class "py-2 px-3" "Arbitrary evaluation — the URL bar is a REPL"))
|
(td :class "py-2 px-3" "Arbitrary evaluation — the URL bar is a REPL"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "py-2 px-3 font-mono text-violet-700" "json")
|
(td :class "py-2 px-3 font-mono text-violet-700" "json")
|
||||||
(td :class "py-2 px-3 font-mono text-sm" "/(json.(language.(doc.primitives)))")
|
(td :class "py-2 px-3 font-mono text-sm" "/sx/(json.(language.(doc.primitives)))")
|
||||||
(td :class "py-2 px-3" "Return data as JSON — pure query mode"))))))
|
(td :class "py-2 px-3" "Return data as JSON — pure query mode"))))))
|
||||||
|
|
||||||
(~doc-section :title "Evaluation Model" :id "eval"
|
(~doc-section :title "Evaluation Model" :id "eval"
|
||||||
@@ -218,7 +218,7 @@
|
|||||||
"known function names are called; unknown symbols self-evaluate to their name as a string; "
|
"known function names are called; unknown symbols self-evaluate to their name as a string; "
|
||||||
"components (" (code "~name") ") are looked up in the component env.")
|
"components (" (code "~name") ") are looked up in the component env.")
|
||||||
(~doc-code :code (highlight
|
(~doc-code :code (highlight
|
||||||
"/(language.(doc.introduction))\n\n;; After dot→space: (language (doc introduction))\n;; 1. Eval `introduction` → not a known function → \"introduction\"\n;; 2. Eval (doc \"introduction\") → call doc(\"introduction\") → page content\n;; 3. Eval (language content) → call language(content) → passes through\n;; 4. Router wraps result in (~sx-doc :path \"(language (doc introduction))\" ...)\n\n/(~essay-sx-sucks)\n;; 1. Eval ~essay-sx-sucks → component lookup → evaluate → content\n;; 2. Router wraps in ~sx-doc"
|
"/sx/(language.(doc.introduction))\n\n;; After dot→space: (language (doc introduction))\n;; 1. Eval `introduction` → not a known function → \"introduction\"\n;; 2. Eval (doc \"introduction\") → call doc(\"introduction\") → page content\n;; 3. Eval (language content) → call language(content) → passes through\n;; 4. Router wraps result in (~sx-doc :path \"(language (doc introduction))\" ...)\n\n/(~essay-sx-sucks)\n;; 1. Eval ~essay-sx-sucks → component lookup → evaluate → content\n;; 2. Router wraps in ~sx-doc"
|
||||||
"lisp"))
|
"lisp"))
|
||||||
|
|
||||||
(~doc-subsection :title "Section Functions"
|
(~doc-subsection :title "Section Functions"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
(~doc-section :title "The Opportunity" :id "opportunity"
|
(~doc-section :title "The Opportunity" :id "opportunity"
|
||||||
(p "SX already has types. Every primitive in " (code "primitives.sx") " declares " (code ":returns \"number\"") " or " (code ":returns \"boolean\"") ". Every IO primitive in " (code "boundary.sx") " declares " (code ":returns \"dict?\"") " or " (code ":returns \"any\"") ". Component params are named. The information exists — nobody checks it.")
|
(p "SX already has types. Every primitive in " (code "primitives.sx") " declares " (code ":returns \"number\"") " or " (code ":returns \"boolean\"") ". Every IO primitive in " (code "boundary.sx") " declares " (code ":returns \"dict?\"") " or " (code ":returns \"any\"") ". Component params are named. The information exists — nobody checks it.")
|
||||||
(p "A gradual type system makes this information useful. Annotations are optional. Unannotated code works exactly as before. Annotated code gets checked at registration time — zero runtime cost, errors before any request is served. The checker is a spec module (" (code "types.sx") "), bootstrapped to every host.")
|
(p "A gradual type system makes this information useful. Annotations are optional. Unannotated code works exactly as before. Annotated code gets checked at registration time — zero runtime cost, errors before any request is served. The checker is a spec module (" (code "types.sx") "), bootstrapped to every host.")
|
||||||
(p "This is not Haskell. SX doesn't need a type system to be correct — " (a :href "/(etc.(plan.theorem-prover))" :class "text-violet-700 underline" "prove.sx") " already verifies primitive properties by exhaustive search. Types serve a different purpose: they catch " (strong "composition errors") " — wrong argument passed to a component, mismatched return type piped into another function, missing keyword arg. The kind of bug you find by reading the stack trace and slapping your forehead."))
|
(p "This is not Haskell. SX doesn't need a type system to be correct — " (a :href "/sx/(etc.(plan.theorem-prover))" :class "text-violet-700 underline" "prove.sx") " already verifies primitive properties by exhaustive search. Types serve a different purpose: they catch " (strong "composition errors") " — wrong argument passed to a component, mismatched return type piped into another function, missing keyword arg. The kind of bug you find by reading the stack trace and slapping your forehead."))
|
||||||
|
|
||||||
;; -----------------------------------------------------------------------
|
;; -----------------------------------------------------------------------
|
||||||
;; What already exists
|
;; What already exists
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
||||||
(li (strong "Runtime values.") " " (code "(if condition 42 \"hello\")") " — the type is " (code "(or number string)") ". The checker doesn't know which branch executes.")
|
(li (strong "Runtime values.") " " (code "(if condition 42 \"hello\")") " — the type is " (code "(or number string)") ". The checker doesn't know which branch executes.")
|
||||||
(li (strong "Dict key presence (yet).") " " (code "(get user \"name\")") " — the checker knows " (code "get") " returns " (code "any") " but doesn't track which keys a dict has. Phase 6 (" (code "deftype") " records) will enable this.")
|
(li (strong "Dict key presence (yet).") " " (code "(get user \"name\")") " — the checker knows " (code "get") " returns " (code "any") " but doesn't track which keys a dict has. Phase 6 (" (code "deftype") " records) will enable this.")
|
||||||
(li (strong "Termination.") " That's " (a :href "/(etc.(plan.theorem-prover))" :class "text-violet-700 underline" "prove.sx") "'s domain.")
|
(li (strong "Termination.") " That's " (a :href "/sx/(etc.(plan.theorem-prover))" :class "text-violet-700 underline" "prove.sx") "'s domain.")
|
||||||
(li (strong "Full algebraic effects.") " The effect system (Phase 7) checks static effect annotations — it does not provide algebraic effect handlers, effect polymorphism, or continuation-based effect dispatch. That door remains open for the future.")))
|
(li (strong "Full algebraic effects.") " The effect system (Phase 7) checks static effect annotations — it does not provide algebraic effect handlers, effect polymorphism, or continuation-based effect dispatch. That door remains open for the future.")))
|
||||||
|
|
||||||
|
|
||||||
@@ -231,7 +231,7 @@
|
|||||||
;; -----------------------------------------------------------------------
|
;; -----------------------------------------------------------------------
|
||||||
|
|
||||||
(~doc-section :title "Types vs Proofs" :id "types-vs-proofs"
|
(~doc-section :title "Types vs Proofs" :id "types-vs-proofs"
|
||||||
(p (a :href "/(etc.(plan.theorem-prover))" :class "text-violet-700 underline" "prove.sx") " and types.sx are complementary, not competing:")
|
(p (a :href "/sx/(etc.(plan.theorem-prover))" :class "text-violet-700 underline" "prove.sx") " and types.sx are complementary, not competing:")
|
||||||
|
|
||||||
(div :class "overflow-x-auto rounded border border-stone-200 mb-4"
|
(div :class "overflow-x-auto rounded border border-stone-200 mb-4"
|
||||||
(table :class "w-full text-left text-sm"
|
(table :class "w-full text-left text-sm"
|
||||||
@@ -483,10 +483,10 @@
|
|||||||
|
|
||||||
(~doc-section :title "Relationships" :id "relationships"
|
(~doc-section :title "Relationships" :id "relationships"
|
||||||
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
(ul :class "list-disc pl-5 text-stone-700 space-y-1"
|
||||||
(li (a :href "/(etc.(plan.theorem-prover))" :class "text-violet-700 underline" "Theorem Prover") " — prove.sx verifies primitive properties; types.sx verifies composition. Complementary.")
|
(li (a :href "/sx/(etc.(plan.theorem-prover))" :class "text-violet-700 underline" "Theorem Prover") " — prove.sx verifies primitive properties; types.sx verifies composition. Complementary.")
|
||||||
(li (a :href "/(etc.(plan.content-addressed-components))" :class "text-violet-700 underline" "Content-Addressed Components") " — component manifests gain type signatures. A consumer knows param types before fetching the source.")
|
(li (a :href "/sx/(etc.(plan.content-addressed-components))" :class "text-violet-700 underline" "Content-Addressed Components") " — component manifests gain type signatures. A consumer knows param types before fetching the source.")
|
||||||
(li (a :href "/(etc.(plan.environment-images))" :class "text-violet-700 underline" "Environment Images") " — the type registry serializes into the image. Type checking happens once at image build time, not on every startup.")
|
(li (a :href "/sx/(etc.(plan.environment-images))" :class "text-violet-700 underline" "Environment Images") " — the type registry serializes into the image. Type checking happens once at image build time, not on every startup.")
|
||||||
(li (a :href "/(etc.(plan.runtime-slicing))" :class "text-violet-700 underline" "Runtime Slicing") " — types.sx is a registration-time module, not a runtime module. It doesn't ship to the client. Zero impact on bundle size."))
|
(li (a :href "/sx/(etc.(plan.runtime-slicing))" :class "text-violet-700 underline" "Runtime Slicing") " — types.sx is a registration-time module, not a runtime module. It doesn't ship to the client. Zero impact on bundle size."))
|
||||||
|
|
||||||
(div :class "rounded border border-amber-200 bg-amber-50 p-3 mt-2"
|
(div :class "rounded border border-amber-200 bg-amber-50 p-3 mt-2"
|
||||||
(p :class "text-amber-800 text-sm" (strong "Depends on: ") "primitives.sx (return + param types), boundary.sx (IO return types + IO classification), eval.sx (defcomp/deftype/defeffect parsing), deps.sx (IO detection — effects formalize this). " (strong "New: ") (code "types.sx") " spec module, type annotations in " (code "parse-comp-params") ", " (code "deftype") " declaration form, " (code "defeffect") " declaration form."))
|
(p :class "text-amber-800 text-sm" (strong "Depends on: ") "primitives.sx (return + param types), boundary.sx (IO return types + IO classification), eval.sx (defcomp/deftype/defeffect parsing), deps.sx (IO detection — effects formalize this). " (strong "New: ") (code "types.sx") " spec module, type annotations in " (code "parse-comp-params") ", " (code "deftype") " declaration form, " (code "defeffect") " declaration form."))
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
(ul :class "space-y-2 text-stone-600 list-disc pl-5"
|
(ul :class "space-y-2 text-stone-600 list-disc pl-5"
|
||||||
(li (strong "Swap inside island: ") "Signals survive. The lake content is replaced but the island's signal closures are untouched. Effects re-bind to new DOM nodes if needed.")
|
(li (strong "Swap inside island: ") "Signals survive. The lake content is replaced but the island's signal closures are untouched. Effects re-bind to new DOM nodes if needed.")
|
||||||
(li (strong "Swap outside island: ") "Signals survive. The island is not affected by swaps to other parts of the page.")
|
(li (strong "Swap outside island: ") "Signals survive. The island is not affected by swaps to other parts of the page.")
|
||||||
(li (strong "Swap replaces island: ") "Signals are " (em "lost") ". The island is disposed. This is where " (a :href "/(geography.(reactive.named-stores))" :sx-get "/(geography.(reactive.named-stores))" :sx-target "#main-panel" :sx-select "#main-panel" :sx-swap "outerHTML" :sx-push-url "true" :class "text-violet-700 underline" "named stores") " come in — they persist at page level, surviving island destruction.")))
|
(li (strong "Swap replaces island: ") "Signals are " (em "lost") ". The island is disposed. This is where " (a :href "/sx/(geography.(reactive.named-stores))" :sx-get "/sx/(geography.(reactive.named-stores))" :sx-target "#main-panel" :sx-select "#main-panel" :sx-swap "outerHTML" :sx-push-url "true" :class "text-violet-700 underline" "named stores") " come in — they persist at page level, surviving island destruction.")))
|
||||||
|
|
||||||
(~doc-section :title "Spec" :id "spec"
|
(~doc-section :title "Spec" :id "spec"
|
||||||
(p "The event bridge is spec'd in " (code "signals.sx") " (sections 12-13). Three functions:")
|
(p "The event bridge is spec'd in " (code "signals.sx") " (sections 12-13). Three functions:")
|
||||||
|
|||||||
@@ -145,7 +145,7 @@
|
|||||||
(td :class "px-3 py-2 text-stone-700" "Phase 2 remaining")
|
(td :class "px-3 py-2 text-stone-700" "Phase 2 remaining")
|
||||||
(td :class "px-3 py-2 text-stone-500 font-medium" "P2")
|
(td :class "px-3 py-2 text-stone-500 font-medium" "P2")
|
||||||
(td :class "px-3 py-2 font-mono text-xs text-stone-500"
|
(td :class "px-3 py-2 font-mono text-xs text-stone-500"
|
||||||
(a :href "/(geography.(reactive.phase2))" :sx-get "/(geography.(reactive.phase2))" :sx-target "#main-panel" :sx-select "#main-panel" :sx-swap "outerHTML" :sx-push-url "true" :class "text-violet-700 underline" "Error boundaries + resource + patterns")))
|
(a :href "/sx/(geography.(reactive.phase2))" :sx-get "/sx/(geography.(reactive.phase2))" :sx-target "#main-panel" :sx-select "#main-panel" :sx-swap "outerHTML" :sx-push-url "true" :class "text-violet-700 underline" "Error boundaries + resource + patterns")))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 text-stone-700" "Error boundaries")
|
(td :class "px-3 py-2 text-stone-700" "Error boundaries")
|
||||||
(td :class "px-3 py-2 text-green-700 font-medium" "Done")
|
(td :class "px-3 py-2 text-green-700 font-medium" "Done")
|
||||||
|
|||||||
@@ -291,7 +291,7 @@
|
|||||||
|
|
||||||
(~demo-marsh-product)
|
(~demo-marsh-product)
|
||||||
|
|
||||||
(~doc-code :code (highlight ";; Island with a store-backed price signal\n(defisland ~demo-marsh-product ()\n (let ((price (def-store \"demo-price\" (fn () (signal 19.99))))\n (qty (signal 1))\n (total (computed (fn () (* (deref price) (deref qty))))))\n (div\n ;; Reactive price display — updates when store changes\n (span \"$\" (deref price))\n (span \"Qty:\") (button \"-\") (span (deref qty)) (button \"+\")\n (span \"Total: $\" (deref total))\n\n ;; Fetch from server — response arrives as hypermedia\n (button :sx-get \"/(geography.(reactive.(api.flash-sale)))\"\n :sx-target \"#marsh-server-msg\"\n :sx-swap \"innerHTML\"\n \"Fetch Price\")\n ;; Server response lands here:\n (div :id \"marsh-server-msg\"))))" "lisp"))
|
(~doc-code :code (highlight ";; Island with a store-backed price signal\n(defisland ~demo-marsh-product ()\n (let ((price (def-store \"demo-price\" (fn () (signal 19.99))))\n (qty (signal 1))\n (total (computed (fn () (* (deref price) (deref qty))))))\n (div\n ;; Reactive price display — updates when store changes\n (span \"$\" (deref price))\n (span \"Qty:\") (button \"-\") (span (deref qty)) (button \"+\")\n (span \"Total: $\" (deref total))\n\n ;; Fetch from server — response arrives as hypermedia\n (button :sx-get \"/sx/(geography.(reactive.(api.flash-sale)))\"\n :sx-target \"#marsh-server-msg\"\n :sx-swap \"innerHTML\"\n \"Fetch Price\")\n ;; Server response lands here:\n (div :id \"marsh-server-msg\"))))" "lisp"))
|
||||||
|
|
||||||
(~doc-code :code (highlight ";; Server returns SX content + a data-init script:\n;;\n;; (<>\n;; (p \"Flash sale! Price: $14.99\")\n;; (script :type \"text/sx\" :data-init\n;; \"(reset! (use-store \\\"demo-price\\\") 14.99)\"))\n;;\n;; The <p> is swapped in as normal hypermedia content.\n;; The script writes to the store signal.\n;; The island's (deref price), total, and savings\n;; all update reactively — no re-render, no diffing." "lisp"))
|
(~doc-code :code (highlight ";; Server returns SX content + a data-init script:\n;;\n;; (<>\n;; (p \"Flash sale! Price: $14.99\")\n;; (script :type \"text/sx\" :data-init\n;; \"(reset! (use-store \\\"demo-price\\\") 14.99)\"))\n;;\n;; The <p> is swapped in as normal hypermedia content.\n;; The script writes to the store signal.\n;; The island's (deref price), total, and savings\n;; all update reactively — no re-render, no diffing." "lisp"))
|
||||||
|
|
||||||
@@ -315,7 +315,7 @@
|
|||||||
(div :id "marsh-flash-target"
|
(div :id "marsh-flash-target"
|
||||||
:class "min-h-[2rem]")
|
:class "min-h-[2rem]")
|
||||||
(button :class "mt-2 px-3 py-1.5 rounded bg-violet-600 text-white text-sm font-medium hover:bg-violet-700"
|
(button :class "mt-2 px-3 py-1.5 rounded bg-violet-600 text-white text-sm font-medium hover:bg-violet-700"
|
||||||
:sx-get "/(geography.(reactive.(api.flash-sale)))"
|
:sx-get "/sx/(geography.(reactive.(api.flash-sale)))"
|
||||||
:sx-target "#marsh-flash-target"
|
:sx-target "#marsh-flash-target"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
"Fetch from server"))
|
"Fetch from server"))
|
||||||
@@ -334,7 +334,7 @@
|
|||||||
|
|
||||||
(~demo-marsh-settle)
|
(~demo-marsh-settle)
|
||||||
|
|
||||||
(~doc-code :code (highlight ";; sx-on-settle runs SX after the swap settles\n(defisland ~demo-marsh-settle ()\n (let ((count (def-store \"settle-count\" (fn () (signal 0)))))\n (div\n ;; Reactive counter — updates from sx-on-settle\n (span \"Fetched: \" (deref count) \" times\")\n\n ;; Button with sx-on-settle hook\n (button :sx-get \"/(geography.(reactive.(api.settle-data)))\"\n :sx-target \"#settle-result\"\n :sx-swap \"innerHTML\"\n :sx-on-settle \"(swap! (use-store \\\"settle-count\\\") inc)\"\n \"Fetch Item\")\n\n ;; Server content lands here (pure hypermedia)\n (div :id \"settle-result\"))))" "lisp"))
|
(~doc-code :code (highlight ";; sx-on-settle runs SX after the swap settles\n(defisland ~demo-marsh-settle ()\n (let ((count (def-store \"settle-count\" (fn () (signal 0)))))\n (div\n ;; Reactive counter — updates from sx-on-settle\n (span \"Fetched: \" (deref count) \" times\")\n\n ;; Button with sx-on-settle hook\n (button :sx-get \"/sx/(geography.(reactive.(api.settle-data)))\"\n :sx-target \"#settle-result\"\n :sx-swap \"innerHTML\"\n :sx-on-settle \"(swap! (use-store \\\"settle-count\\\") inc)\"\n \"Fetch Item\")\n\n ;; Server content lands here (pure hypermedia)\n (div :id \"settle-result\"))))" "lisp"))
|
||||||
|
|
||||||
(p "The server knows nothing about signals or counters. It returns plain content. The " (code "sx-on-settle") " hook is a client-side concern — it runs in the global SX environment with access to all primitives."))
|
(p "The server knows nothing about signals or counters. It returns plain content. The " (code "sx-on-settle") " hook is a client-side concern — it runs in the global SX environment with access to all primitives."))
|
||||||
|
|
||||||
@@ -348,7 +348,7 @@
|
|||||||
|
|
||||||
(~demo-marsh-signal-url)
|
(~demo-marsh-signal-url)
|
||||||
|
|
||||||
(~doc-code :code (highlight ";; sx-get URL computed from a signal\n(defisland ~demo-marsh-signal-url ()\n (let ((mode (signal \"products\"))\n (query (signal \"\")))\n (div\n ;; Mode selector — changes what we're searching\n (div :class \"flex gap-2\"\n (button :on-click (fn (e) (reset! mode \"products\"))\n :class (computed (fn () ...active-class...))\n \"Products\")\n (button :on-click (fn (e) (reset! mode \"events\")) \"Events\")\n (button :on-click (fn (e) (reset! mode \"posts\")) \"Posts\"))\n\n ;; Search button — URL is a computed expression\n (button :sx-get (computed (fn ()\n (str \"/(geography.(reactive.(api.search-\"\n (deref mode) \")))\" \"?q=\" (deref query))))\n :sx-target \"#signal-results\"\n :sx-swap \"innerHTML\"\n \"Search\")\n\n (div :id \"signal-results\"))))" "lisp"))
|
(~doc-code :code (highlight ";; sx-get URL computed from a signal\n(defisland ~demo-marsh-signal-url ()\n (let ((mode (signal \"products\"))\n (query (signal \"\")))\n (div\n ;; Mode selector — changes what we're searching\n (div :class \"flex gap-2\"\n (button :on-click (fn (e) (reset! mode \"products\"))\n :class (computed (fn () ...active-class...))\n \"Products\")\n (button :on-click (fn (e) (reset! mode \"events\")) \"Events\")\n (button :on-click (fn (e) (reset! mode \"posts\")) \"Posts\"))\n\n ;; Search button — URL is a computed expression\n (button :sx-get (computed (fn ()\n (str \"/sx/(geography.(reactive.(api.search-\"\n (deref mode) \")))\" \"?q=\" (deref query))))\n :sx-target \"#signal-results\"\n :sx-swap \"innerHTML\"\n \"Search\")\n\n (div :id \"signal-results\"))))" "lisp"))
|
||||||
|
|
||||||
(p "No custom plumbing. The same " (code "reactive-attr") " mechanism that makes " (code ":class") " reactive also makes " (code ":sx-get") " reactive. " (code "get-verb-info") " reads " (code "dom-get-attr") " at trigger time — it sees the current URL because the effect already updated the DOM attribute."))
|
(p "No custom plumbing. The same " (code "reactive-attr") " mechanism that makes " (code ":class") " reactive also makes " (code ":sx-get") " reactive. " (code "get-verb-info") " reads " (code "dom-get-attr") " at trigger time — it sees the current URL because the effect already updated the DOM attribute."))
|
||||||
|
|
||||||
@@ -361,7 +361,7 @@
|
|||||||
|
|
||||||
(~demo-marsh-view-transform)
|
(~demo-marsh-view-transform)
|
||||||
|
|
||||||
(~doc-code :code (highlight ";; View mode transforms display without refetch\n(defisland ~demo-marsh-view-transform ()\n (let ((view (signal \"list\"))\n (items (signal nil)))\n (div\n ;; View toggle\n (div :class \"flex gap-2\"\n (button :on-click (fn (e) (reset! view \"list\")) \"List\")\n (button :on-click (fn (e) (reset! view \"grid\")) \"Grid\")\n (button :on-click (fn (e) (reset! view \"compact\")) \"Compact\"))\n\n ;; Fetch from server — stores raw data in signal\n (button :sx-get \"/(geography.(reactive.(api.catalog)))\"\n :sx-target \"#catalog-raw\"\n :sx-swap \"innerHTML\"\n \"Fetch Catalog\")\n\n ;; Raw server content (hidden, used as data source)\n (div :id \"catalog-raw\" :class \"hidden\")\n\n ;; Reactive display — re-renders when view changes\n (div (computed (fn () (render-view (deref view) (deref items))))))))" "lisp"))
|
(~doc-code :code (highlight ";; View mode transforms display without refetch\n(defisland ~demo-marsh-view-transform ()\n (let ((view (signal \"list\"))\n (items (signal nil)))\n (div\n ;; View toggle\n (div :class \"flex gap-2\"\n (button :on-click (fn (e) (reset! view \"list\")) \"List\")\n (button :on-click (fn (e) (reset! view \"grid\")) \"Grid\")\n (button :on-click (fn (e) (reset! view \"compact\")) \"Compact\"))\n\n ;; Fetch from server — stores raw data in signal\n (button :sx-get \"/sx/(geography.(reactive.(api.catalog)))\"\n :sx-target \"#catalog-raw\"\n :sx-swap \"innerHTML\"\n \"Fetch Catalog\")\n\n ;; Raw server content (hidden, used as data source)\n (div :id \"catalog-raw\" :class \"hidden\")\n\n ;; Reactive display — re-renders when view changes\n (div (computed (fn () (render-view (deref view) (deref items))))))))" "lisp"))
|
||||||
|
|
||||||
(p "The view signal doesn't just toggle CSS classes — it fundamentally reshapes the DOM. List view shows description. Grid view arranges in columns. Compact view shows names only. All from the same server data, transformed by client state."))
|
(p "The view signal doesn't just toggle CSS classes — it fundamentally reshapes the DOM. List view shows description. Grid view arranges in columns. Compact view shows names only. All from the same server data, transformed by client state."))
|
||||||
|
|
||||||
@@ -408,7 +408,7 @@
|
|||||||
(div :class "border-t border-stone-200 pt-3"
|
(div :class "border-t border-stone-200 pt-3"
|
||||||
(div :class "flex items-center gap-3"
|
(div :class "flex items-center gap-3"
|
||||||
(button :class "px-4 py-2 rounded bg-violet-600 text-white text-sm font-medium hover:bg-violet-700"
|
(button :class "px-4 py-2 rounded bg-violet-600 text-white text-sm font-medium hover:bg-violet-700"
|
||||||
:sx-get "/(geography.(reactive.(api.flash-sale)))"
|
:sx-get "/sx/(geography.(reactive.(api.flash-sale)))"
|
||||||
:sx-target "#marsh-server-msg"
|
:sx-target "#marsh-server-msg"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
"Fetch Price from Server")
|
"Fetch Price from Server")
|
||||||
@@ -471,7 +471,7 @@
|
|||||||
(span :class "text-sm text-stone-600" " times")))
|
(span :class "text-sm text-stone-600" " times")))
|
||||||
(div :class "flex items-center gap-3"
|
(div :class "flex items-center gap-3"
|
||||||
(button :class "px-4 py-2 rounded bg-violet-600 text-white text-sm font-medium hover:bg-violet-700"
|
(button :class "px-4 py-2 rounded bg-violet-600 text-white text-sm font-medium hover:bg-violet-700"
|
||||||
:sx-get "/(geography.(reactive.(api.settle-data)))"
|
:sx-get "/sx/(geography.(reactive.(api.settle-data)))"
|
||||||
:sx-target "#settle-result"
|
:sx-target "#settle-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-on-settle "(swap! (use-store \"settle-count\") inc)"
|
:sx-on-settle "(swap! (use-store \"settle-count\") inc)"
|
||||||
@@ -516,7 +516,7 @@
|
|||||||
:class "flex-1 px-3 py-1.5 rounded border border-stone-300 text-sm focus:outline-none focus:border-violet-400")
|
:class "flex-1 px-3 py-1.5 rounded border border-stone-300 text-sm focus:outline-none focus:border-violet-400")
|
||||||
(button :class "px-4 py-1.5 rounded bg-violet-600 text-white text-sm font-medium hover:bg-violet-700"
|
(button :class "px-4 py-1.5 rounded bg-violet-600 text-white text-sm font-medium hover:bg-violet-700"
|
||||||
:sx-get (computed (fn ()
|
:sx-get (computed (fn ()
|
||||||
(str "/(geography.(reactive.(api.search-" (deref mode)
|
(str "/sx/(geography.(reactive.(api.search-" (deref mode)
|
||||||
")))""?q=" (deref query))))
|
")))""?q=" (deref query))))
|
||||||
:sx-target "#signal-results"
|
:sx-target "#signal-results"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
@@ -524,7 +524,7 @@
|
|||||||
;; Current URL display
|
;; Current URL display
|
||||||
(p :class "text-xs text-stone-400 font-mono"
|
(p :class "text-xs text-stone-400 font-mono"
|
||||||
"URL: " (computed (fn ()
|
"URL: " (computed (fn ()
|
||||||
(str "/(geography.(reactive.(api.search-" (deref mode) ")))" "?q=" (deref query)))))
|
(str "/sx/(geography.(reactive.(api.search-" (deref mode) ")))" "?q=" (deref query)))))
|
||||||
;; Results
|
;; Results
|
||||||
(div :id "signal-results" :class "min-h-[3rem] rounded bg-stone-50 p-2"
|
(div :id "signal-results" :class "min-h-[3rem] rounded bg-stone-50 p-2"
|
||||||
(p :class "text-sm text-stone-400 italic" "Select a category and search.")))))
|
(p :class "text-sm text-stone-400 italic" "Select a category and search.")))))
|
||||||
@@ -561,7 +561,7 @@
|
|||||||
;; Fetch button — response writes structured data to store via data-init
|
;; Fetch button — response writes structured data to store via data-init
|
||||||
(div :class "flex items-center gap-3"
|
(div :class "flex items-center gap-3"
|
||||||
(button :class "px-4 py-2 rounded bg-emerald-600 text-white text-sm font-medium hover:bg-emerald-700"
|
(button :class "px-4 py-2 rounded bg-emerald-600 text-white text-sm font-medium hover:bg-emerald-700"
|
||||||
:sx-get "/(geography.(reactive.(api.catalog)))"
|
:sx-get "/sx/(geography.(reactive.(api.catalog)))"
|
||||||
:sx-target "#catalog-msg"
|
:sx-target "#catalog-msg"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
"Fetch Catalog")
|
"Fetch Catalog")
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
|
|
||||||
(~doc-section :title "htmx Lakes" :id "lakes"
|
(~doc-section :title "htmx Lakes" :id "lakes"
|
||||||
(p "An htmx lake is server-driven content " (em "inside") " a reactive island. The island provides the reactive boundary; the lake content is swapped via " (code "sx-get") "/" (code "sx-post") " like normal hypermedia.")
|
(p "An htmx lake is server-driven content " (em "inside") " a reactive island. The island provides the reactive boundary; the lake content is swapped via " (code "sx-get") "/" (code "sx-post") " like normal hypermedia.")
|
||||||
(p "This works because signals live in JavaScript closures, not in the DOM. When a swap replaces lake content, the island's signals are unaffected. The lake can communicate back to the island via the " (a :href "/(geography.(reactive.event-bridge))" :sx-get "/(geography.(reactive.event-bridge))" :sx-target "#main-panel" :sx-select "#main-panel" :sx-swap "outerHTML" :sx-push-url "true" :class "text-violet-700 underline" "event bridge") ".")
|
(p "This works because signals live in JavaScript closures, not in the DOM. When a swap replaces lake content, the island's signals are unaffected. The lake can communicate back to the island via the " (a :href "/sx/(geography.(reactive.event-bridge))" :sx-get "/sx/(geography.(reactive.event-bridge))" :sx-target "#main-panel" :sx-select "#main-panel" :sx-swap "outerHTML" :sx-push-url "true" :class "text-violet-700 underline" "event bridge") ".")
|
||||||
|
|
||||||
(~doc-subsection :title "Navigation scenarios"
|
(~doc-subsection :title "Navigation scenarios"
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
@@ -155,7 +155,7 @@
|
|||||||
(li (strong "Spec-first.") " Signal semantics live in " (code "signals.sx") ". Bootstrapped to JS and Python. The same primitives will work in future hosts — Go, Rust, native.")
|
(li (strong "Spec-first.") " Signal semantics live in " (code "signals.sx") ". Bootstrapped to JS and Python. The same primitives will work in future hosts — Go, Rust, native.")
|
||||||
(li (strong "No build step.") " Reactive bindings are created at runtime during DOM rendering. No JSX compilation, no Babel transforms, no Vite plugins."))
|
(li (strong "No build step.") " Reactive bindings are created at runtime during DOM rendering. No JSX compilation, no Babel transforms, no Vite plugins."))
|
||||||
|
|
||||||
(p :class "mt-4" "The recommendation from the " (a :href "/(etc.(essay.client-reactivity))" :class "text-violet-700 underline" "Client Reactivity") " essay was: \"Tier 4 probably never.\" This plan is what happens when the answer changes. The design avoids every footgun that essay warns about — no useState cascading to useEffect cascading to Context cascading to a state management library. Signals are one primitive. Islands are one boundary. The rest is composition."))))
|
(p :class "mt-4" "The recommendation from the " (a :href "/sx/(etc.(essay.client-reactivity))" :class "text-violet-700 underline" "Client Reactivity") " essay was: \"Tier 4 probably never.\" This plan is what happens when the answer changes. The design avoids every footgun that essay warns about — no useState cascading to useEffect cascading to Context cascading to a state management library. Signals are one primitive. Islands are one boundary. The rest is composition."))))
|
||||||
|
|
||||||
|
|
||||||
;; ---------------------------------------------------------------------------
|
;; ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -14,9 +14,9 @@
|
|||||||
"Pages with data dependencies fall back to "
|
"Pages with data dependencies fall back to "
|
||||||
(span :class "text-amber-700 font-medium" "server fetch")
|
(span :class "text-amber-700 font-medium" "server fetch")
|
||||||
" transparently. Powered by "
|
" transparently. Powered by "
|
||||||
(a :href "/(language.(spec.router))" :class "text-violet-700 underline" "router.sx")
|
(a :href "/sx/(language.(spec.router))" :class "text-violet-700 underline" "router.sx")
|
||||||
" route matching and "
|
" route matching and "
|
||||||
(a :href "/(language.(spec.deps))" :class "text-violet-700 underline" "deps.sx")
|
(a :href "/sx/(language.(spec.deps))" :class "text-violet-700 underline" "deps.sx")
|
||||||
" IO detection.")
|
" IO detection.")
|
||||||
|
|
||||||
(div :class "mb-8 grid grid-cols-4 gap-4"
|
(div :class "mb-8 grid grid-cols-4 gap-4"
|
||||||
|
|||||||
@@ -35,8 +35,8 @@
|
|||||||
(div
|
(div
|
||||||
(h1 :class "text-2xl font-bold text-stone-800" title)
|
(h1 :class "text-2xl font-bold text-stone-800" title)
|
||||||
(p :class "text-sm text-stone-500 mt-1" desc))
|
(p :class "text-sm text-stone-500 mt-1" desc))
|
||||||
(a :href (str "/(language.(spec." slug "))")
|
(a :href (str "/sx/(language.(spec." slug "))")
|
||||||
:sx-get (str "/(language.(spec." slug "))")
|
:sx-get (str "/sx/(language.(spec." slug "))")
|
||||||
:sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
:class "text-sm text-violet-600 hover:text-violet-800 font-medium"
|
:class "text-sm text-violet-600 hover:text-violet-800 font-medium"
|
||||||
|
|||||||
@@ -32,36 +32,36 @@
|
|||||||
(tbody
|
(tbody
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(spec.parser))" :class "hover:underline"
|
(a :href "/sx/(language.(spec.parser))" :class "hover:underline"
|
||||||
:sx-get "/(language.(spec.parser))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(language.(spec.parser))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
"parser.sx"))
|
"parser.sx"))
|
||||||
(td :class "px-3 py-2 text-stone-700" "Tokenization and parsing of SX source text into AST"))
|
(td :class "px-3 py-2 text-stone-700" "Tokenization and parsing of SX source text into AST"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(spec.evaluator))" :class "hover:underline"
|
(a :href "/sx/(language.(spec.evaluator))" :class "hover:underline"
|
||||||
:sx-get "/(language.(spec.evaluator))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(language.(spec.evaluator))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
"eval.sx"))
|
"eval.sx"))
|
||||||
(td :class "px-3 py-2 text-stone-700" "Tree-walking evaluation of SX expressions"))
|
(td :class "px-3 py-2 text-stone-700" "Tree-walking evaluation of SX expressions"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(spec.primitives))" :class "hover:underline"
|
(a :href "/sx/(language.(spec.primitives))" :class "hover:underline"
|
||||||
:sx-get "/(language.(spec.primitives))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(language.(spec.primitives))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
"primitives.sx"))
|
"primitives.sx"))
|
||||||
(td :class "px-3 py-2 text-stone-700" "All built-in pure functions and their signatures"))
|
(td :class "px-3 py-2 text-stone-700" "All built-in pure functions and their signatures"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(spec.special-forms))" :class "hover:underline"
|
(a :href "/sx/(language.(spec.special-forms))" :class "hover:underline"
|
||||||
:sx-get "/(language.(spec.special-forms))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(language.(spec.special-forms))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
"special-forms.sx"))
|
"special-forms.sx"))
|
||||||
(td :class "px-3 py-2 text-stone-700" "All special forms — syntactic constructs with custom evaluation rules"))
|
(td :class "px-3 py-2 text-stone-700" "All special forms — syntactic constructs with custom evaluation rules"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(spec.renderer))" :class "hover:underline"
|
(a :href "/sx/(language.(spec.renderer))" :class "hover:underline"
|
||||||
:sx-get "/(language.(spec.renderer))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(language.(spec.renderer))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
"render.sx"))
|
"render.sx"))
|
||||||
(td :class "px-3 py-2 text-stone-700" "Shared rendering registries and utilities used by all adapters"))))))
|
(td :class "px-3 py-2 text-stone-700" "Shared rendering registries and utilities used by all adapters"))))))
|
||||||
@@ -79,32 +79,32 @@
|
|||||||
(tbody
|
(tbody
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(spec.adapter-dom))" :class "hover:underline"
|
(a :href "/sx/(language.(spec.adapter-dom))" :class "hover:underline"
|
||||||
:sx-get "/(language.(spec.adapter-dom))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(language.(spec.adapter-dom))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
"adapter-dom.sx"))
|
"adapter-dom.sx"))
|
||||||
(td :class "px-3 py-2 text-stone-700" "Live DOM nodes")
|
(td :class "px-3 py-2 text-stone-700" "Live DOM nodes")
|
||||||
(td :class "px-3 py-2 text-stone-500" "Browser"))
|
(td :class "px-3 py-2 text-stone-500" "Browser"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(spec.adapter-html))" :class "hover:underline"
|
(a :href "/sx/(language.(spec.adapter-html))" :class "hover:underline"
|
||||||
:sx-get "/(language.(spec.adapter-html))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(language.(spec.adapter-html))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
"adapter-html.sx"))
|
"adapter-html.sx"))
|
||||||
(td :class "px-3 py-2 text-stone-700" "HTML strings")
|
(td :class "px-3 py-2 text-stone-700" "HTML strings")
|
||||||
(td :class "px-3 py-2 text-stone-500" "Server"))
|
(td :class "px-3 py-2 text-stone-500" "Server"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(spec.adapter-sx))" :class "hover:underline"
|
(a :href "/sx/(language.(spec.adapter-sx))" :class "hover:underline"
|
||||||
:sx-get "/(language.(spec.adapter-sx))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(language.(spec.adapter-sx))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
"adapter-sx.sx"))
|
"adapter-sx.sx"))
|
||||||
(td :class "px-3 py-2 text-stone-700" "SX wire format")
|
(td :class "px-3 py-2 text-stone-700" "SX wire format")
|
||||||
(td :class "px-3 py-2 text-stone-500" "Server to client"))
|
(td :class "px-3 py-2 text-stone-500" "Server to client"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(spec.adapter-async))" :class "hover:underline"
|
(a :href "/sx/(language.(spec.adapter-async))" :class "hover:underline"
|
||||||
:sx-get "/(language.(spec.adapter-async))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(language.(spec.adapter-async))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
"adapter-async.sx"))
|
"adapter-async.sx"))
|
||||||
(td :class "px-3 py-2 text-stone-700" "HTML/SX with awaited I/O")
|
(td :class "px-3 py-2 text-stone-700" "HTML/SX with awaited I/O")
|
||||||
@@ -122,29 +122,29 @@
|
|||||||
(tbody
|
(tbody
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(spec.engine))" :class "hover:underline"
|
(a :href "/sx/(language.(spec.engine))" :class "hover:underline"
|
||||||
:sx-get "/(language.(spec.engine))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(language.(spec.engine))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
"engine.sx"))
|
"engine.sx"))
|
||||||
(td :class "px-3 py-2 text-stone-700" "Pure logic — trigger parsing, swap algorithms, morph, history, SSE, indicators"))
|
(td :class "px-3 py-2 text-stone-700" "Pure logic — trigger parsing, swap algorithms, morph, history, SSE, indicators"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(spec.orchestration))" :class "hover:underline"
|
(a :href "/sx/(language.(spec.orchestration))" :class "hover:underline"
|
||||||
:sx-get "/(language.(spec.orchestration))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(language.(spec.orchestration))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
"orchestration.sx"))
|
"orchestration.sx"))
|
||||||
(td :class "px-3 py-2 text-stone-700" "Browser wiring — binds engine to DOM events, fetch, request lifecycle"))
|
(td :class "px-3 py-2 text-stone-700" "Browser wiring — binds engine to DOM events, fetch, request lifecycle"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(spec.boot))" :class "hover:underline"
|
(a :href "/sx/(language.(spec.boot))" :class "hover:underline"
|
||||||
:sx-get "/(language.(spec.boot))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(language.(spec.boot))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
"boot.sx"))
|
"boot.sx"))
|
||||||
(td :class "px-3 py-2 text-stone-700" "Browser startup — mount, hydrate, script processing, head hoisting"))
|
(td :class "px-3 py-2 text-stone-700" "Browser startup — mount, hydrate, script processing, head hoisting"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(spec.router))" :class "hover:underline"
|
(a :href "/sx/(language.(spec.router))" :class "hover:underline"
|
||||||
:sx-get "/(language.(spec.router))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(language.(spec.router))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
"router.sx"))
|
"router.sx"))
|
||||||
(td :class "px-3 py-2 text-stone-700" "Client-side route matching — Flask-style patterns, param extraction"))))))
|
(td :class "px-3 py-2 text-stone-700" "Client-side route matching — Flask-style patterns, param extraction"))))))
|
||||||
@@ -161,8 +161,8 @@
|
|||||||
(tbody
|
(tbody
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(spec.signals))" :class "hover:underline"
|
(a :href "/sx/(language.(spec.signals))" :class "hover:underline"
|
||||||
:sx-get "/(language.(spec.signals))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(language.(spec.signals))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
"signals.sx"))
|
"signals.sx"))
|
||||||
(td :class "px-3 py-2 text-stone-700" "Signal runtime — signal, deref, reset!, swap!, computed, effect, batch, island scope"))))))
|
(td :class "px-3 py-2 text-stone-700" "Signal runtime — signal, deref, reset!, swap!, computed, effect, batch, island scope"))))))
|
||||||
@@ -179,22 +179,22 @@
|
|||||||
(tbody
|
(tbody
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(spec.boundary))" :class "hover:underline"
|
(a :href "/sx/(language.(spec.boundary))" :class "hover:underline"
|
||||||
:sx-get "/(language.(spec.boundary))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(language.(spec.boundary))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
"boundary.sx"))
|
"boundary.sx"))
|
||||||
(td :class "px-3 py-2 text-stone-700" "Language boundary — I/O primitives, page helpers, tier declarations"))
|
(td :class "px-3 py-2 text-stone-700" "Language boundary — I/O primitives, page helpers, tier declarations"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(spec.forms))" :class "hover:underline"
|
(a :href "/sx/(language.(spec.forms))" :class "hover:underline"
|
||||||
:sx-get "/(language.(spec.forms))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(language.(spec.forms))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
"forms.sx"))
|
"forms.sx"))
|
||||||
(td :class "px-3 py-2 text-stone-700" "Definition forms — defhandler, defquery, defaction, defpage"))
|
(td :class "px-3 py-2 text-stone-700" "Definition forms — defhandler, defquery, defaction, defpage"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(spec.page-helpers))" :class "hover:underline"
|
(a :href "/sx/(language.(spec.page-helpers))" :class "hover:underline"
|
||||||
:sx-get "/(language.(spec.page-helpers))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(language.(spec.page-helpers))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
"page-helpers.sx"))
|
"page-helpers.sx"))
|
||||||
(td :class "px-3 py-2 text-stone-700" "Pure data-transformation helpers for page rendering"))))))
|
(td :class "px-3 py-2 text-stone-700" "Pure data-transformation helpers for page rendering"))))))
|
||||||
@@ -248,29 +248,29 @@ deps.sx depends on: eval")))
|
|||||||
(tbody
|
(tbody
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(spec.continuations))" :class "hover:underline"
|
(a :href "/sx/(language.(spec.continuations))" :class "hover:underline"
|
||||||
:sx-get "/(language.(spec.continuations))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(language.(spec.continuations))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
"continuations.sx"))
|
"continuations.sx"))
|
||||||
(td :class "px-3 py-2 text-stone-700" "Delimited continuations — shift/reset"))
|
(td :class "px-3 py-2 text-stone-700" "Delimited continuations — shift/reset"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(spec.callcc))" :class "hover:underline"
|
(a :href "/sx/(language.(spec.callcc))" :class "hover:underline"
|
||||||
:sx-get "/(language.(spec.callcc))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(language.(spec.callcc))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
"callcc.sx"))
|
"callcc.sx"))
|
||||||
(td :class "px-3 py-2 text-stone-700" "Full first-class continuations — call/cc"))
|
(td :class "px-3 py-2 text-stone-700" "Full first-class continuations — call/cc"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(spec.types))" :class "hover:underline"
|
(a :href "/sx/(language.(spec.types))" :class "hover:underline"
|
||||||
:sx-get "/(language.(spec.types))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(language.(spec.types))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
"types.sx"))
|
"types.sx"))
|
||||||
(td :class "px-3 py-2 text-stone-700" "Gradual type system — registration-time checking, zero runtime cost"))
|
(td :class "px-3 py-2 text-stone-700" "Gradual type system — registration-time checking, zero runtime cost"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(spec.deps))" :class "hover:underline"
|
(a :href "/sx/(language.(spec.deps))" :class "hover:underline"
|
||||||
:sx-get "/(language.(spec.deps))" :sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-get "/sx/(language.(spec.deps))" :sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
"deps.sx"))
|
"deps.sx"))
|
||||||
(td :class "px-3 py-2 text-stone-700" "Component dependency analysis — bundling, IO detection, CSS scoping"))))))
|
(td :class "px-3 py-2 text-stone-700" "Component dependency analysis — bundling, IO detection, CSS scoping"))))))
|
||||||
@@ -337,8 +337,8 @@ deps.sx depends on: eval")))
|
|||||||
(div :class "flex items-center gap-3 mb-4"
|
(div :class "flex items-center gap-3 mb-4"
|
||||||
(span :class "text-sm text-stone-400 font-mono" spec-filename)
|
(span :class "text-sm text-stone-400 font-mono" spec-filename)
|
||||||
(span :class "text-sm text-stone-500 flex-1" spec-desc)
|
(span :class "text-sm text-stone-500 flex-1" spec-desc)
|
||||||
(a :href (str "/(language.(spec.(explore." (replace spec-filename ".sx" "") ")))")
|
(a :href (str "/sx/(language.(spec.(explore." (replace spec-filename ".sx" "") ")))")
|
||||||
:sx-get (str "/(language.(spec.(explore." (replace spec-filename ".sx" "") ")))")
|
:sx-get (str "/sx/(language.(spec.(explore." (replace spec-filename ".sx" "") ")))")
|
||||||
:sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
:class "text-sm text-violet-600 hover:text-violet-800 font-medium whitespace-nowrap"
|
:class "text-sm text-violet-600 hover:text-violet-800 font-medium whitespace-nowrap"
|
||||||
@@ -380,21 +380,21 @@ deps.sx depends on: eval")))
|
|||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 text-stone-700" "JavaScript")
|
(td :class "px-3 py-2 text-stone-700" "JavaScript")
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(bootstrapper.javascript))" :class "hover:underline"
|
(a :href "/sx/(language.(bootstrapper.javascript))" :class "hover:underline"
|
||||||
"bootstrap_js.py"))
|
"bootstrap_js.py"))
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-stone-500" "sx-browser.js")
|
(td :class "px-3 py-2 font-mono text-sm text-stone-500" "sx-browser.js")
|
||||||
(td :class "px-3 py-2 text-green-600" "Live"))
|
(td :class "px-3 py-2 text-green-600" "Live"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "px-3 py-2 text-stone-700" "Python")
|
(td :class "px-3 py-2 text-stone-700" "Python")
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(bootstrapper.python))" :class "hover:underline"
|
(a :href "/sx/(language.(bootstrapper.python))" :class "hover:underline"
|
||||||
"bootstrap_py.py"))
|
"bootstrap_py.py"))
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-stone-500" "sx_ref.py")
|
(td :class "px-3 py-2 font-mono text-sm text-stone-500" "sx_ref.py")
|
||||||
(td :class "px-3 py-2 text-green-600" "Live"))
|
(td :class "px-3 py-2 text-green-600" "Live"))
|
||||||
(tr :class "border-b border-stone-100 bg-green-50"
|
(tr :class "border-b border-stone-100 bg-green-50"
|
||||||
(td :class "px-3 py-2 text-stone-700" "Python (self-hosting)")
|
(td :class "px-3 py-2 text-stone-700" "Python (self-hosting)")
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||||
(a :href "/(language.(bootstrapper.self-hosting))" :class "hover:underline"
|
(a :href "/sx/(language.(bootstrapper.self-hosting))" :class "hover:underline"
|
||||||
"py.sx"))
|
"py.sx"))
|
||||||
(td :class "px-3 py-2 font-mono text-sm text-stone-500" "sx_ref.py")
|
(td :class "px-3 py-2 font-mono text-sm text-stone-500" "sx_ref.py")
|
||||||
(td :class "px-3 py-2 text-green-600" "G0 == G1"))
|
(td :class "px-3 py-2 text-green-600" "G0 == G1"))
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
"a nested function call that the server evaluates to produce a page.")
|
"a nested function call that the server evaluates to produce a page.")
|
||||||
(p "Every page on this site is addressed by an SX URL. You are currently reading:")
|
(p "Every page on this site is addressed by an SX URL. You are currently reading:")
|
||||||
(~doc-code :code (highlight
|
(~doc-code :code (highlight
|
||||||
"/(applications.(sx-urls))"
|
"/sx/(applications.(sx-urls))"
|
||||||
"lisp"))
|
"lisp"))
|
||||||
(p "This is a function call: " (code "applications") " is called with the result of "
|
(p "This is a function call: " (code "applications") " is called with the result of "
|
||||||
(code "(sx-urls)") ". The server evaluates it, wraps the content in a layout, "
|
(code "(sx-urls)") ". The server evaluates it, wraps the content in a layout, "
|
||||||
@@ -73,13 +73,13 @@
|
|||||||
"Called with no arguments, they return their index page. "
|
"Called with no arguments, they return their index page. "
|
||||||
"Called with content, they pass it through:")
|
"Called with content, they pass it through:")
|
||||||
(~doc-code :code (highlight
|
(~doc-code :code (highlight
|
||||||
"/(geography) ;; geography section index\n/(language) ;; language section index\n/(applications) ;; applications section index\n/(etc) ;; etc section index"
|
"/sx/(geography) ;; geography section index\n/(language) ;; language section index\n/(applications) ;; applications section index\n/(etc) ;; etc section index"
|
||||||
"lisp")))
|
"lisp")))
|
||||||
|
|
||||||
(~doc-subsection :title "Sub-sections"
|
(~doc-subsection :title "Sub-sections"
|
||||||
(p "Sub-sections nest inside sections:")
|
(p "Sub-sections nest inside sections:")
|
||||||
(~doc-code :code (highlight
|
(~doc-code :code (highlight
|
||||||
"/(geography.(hypermedia)) ;; hypermedia sub-section index\n/(geography.(reactive)) ;; reactive islands sub-section\n/(geography.(isomorphism)) ;; isomorphism sub-section\n/(language.(doc)) ;; documentation sub-section\n/(language.(spec)) ;; specification sub-section\n/(language.(bootstrapper)) ;; bootstrappers sub-section\n/(applications.(cssx)) ;; CSSX sub-section\n/(applications.(protocol)) ;; protocols sub-section"
|
"/sx/(geography.(hypermedia)) ;; hypermedia sub-section index\n/(geography.(reactive)) ;; reactive islands sub-section\n/(geography.(isomorphism)) ;; isomorphism sub-section\n/(language.(doc)) ;; documentation sub-section\n/(language.(spec)) ;; specification sub-section\n/(language.(bootstrapper)) ;; bootstrappers sub-section\n/(applications.(cssx)) ;; CSSX sub-section\n/(applications.(protocol)) ;; protocols sub-section"
|
||||||
"lisp")))
|
"lisp")))
|
||||||
|
|
||||||
(~doc-subsection :title "Leaf pages"
|
(~doc-subsection :title "Leaf pages"
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
"lisp"))
|
"lisp"))
|
||||||
(p "New components are URL-accessible the moment they are defined. "
|
(p "New components are URL-accessible the moment they are defined. "
|
||||||
"No registration, no routing table update, no deploy. "
|
"No registration, no routing table update, no deploy. "
|
||||||
"Define a " (code "defcomp") ", refresh the page, visit " (code "/(~your-component)") "."))
|
"Define a " (code "defcomp") ", refresh the page, visit " (code "/sx/(~your-component)") "."))
|
||||||
|
|
||||||
;; -----------------------------------------------------------------
|
;; -----------------------------------------------------------------
|
||||||
(~doc-section :title "Relative URLs" :id "relative"
|
(~doc-section :title "Relative URLs" :id "relative"
|
||||||
@@ -186,27 +186,27 @@
|
|||||||
(tbody :class "text-stone-600"
|
(tbody :class "text-stone-600"
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "py-2 px-3 font-mono text-violet-700" "!source")
|
(td :class "py-2 px-3 font-mono text-violet-700" "!source")
|
||||||
(td :class "py-2 px-3 font-mono text-sm" "/(!source.(~essay-sx-sucks))")
|
(td :class "py-2 px-3 font-mono text-sm" "/sx/(!source.(~essay-sx-sucks))")
|
||||||
(td :class "py-2 px-3" "Show the defcomp source code"))
|
(td :class "py-2 px-3" "Show the defcomp source code"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "py-2 px-3 font-mono text-violet-700" "!inspect")
|
(td :class "py-2 px-3 font-mono text-violet-700" "!inspect")
|
||||||
(td :class "py-2 px-3 font-mono text-sm" "/(!inspect.(language.(doc.primitives)))")
|
(td :class "py-2 px-3 font-mono text-sm" "/sx/(!inspect.(language.(doc.primitives)))")
|
||||||
(td :class "py-2 px-3" "Dependencies, CSS, render plan, I/O"))
|
(td :class "py-2 px-3" "Dependencies, CSS, render plan, I/O"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "py-2 px-3 font-mono text-violet-700" "!diff")
|
(td :class "py-2 px-3 font-mono text-violet-700" "!diff")
|
||||||
(td :class "py-2 px-3 font-mono text-sm" "/(!diff.(spec.signals).(spec.eval))")
|
(td :class "py-2 px-3 font-mono text-sm" "/sx/(!diff.(spec.signals).(spec.eval))")
|
||||||
(td :class "py-2 px-3" "Side-by-side comparison"))
|
(td :class "py-2 px-3" "Side-by-side comparison"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "py-2 px-3 font-mono text-violet-700" "!search")
|
(td :class "py-2 px-3 font-mono text-violet-700" "!search")
|
||||||
(td :class "py-2 px-3 font-mono text-sm" "/(!search.\"define\".:in.(spec.signals))")
|
(td :class "py-2 px-3 font-mono text-sm" "/sx/(!search.\"define\".:in.(spec.signals))")
|
||||||
(td :class "py-2 px-3" "Grep within a page or spec"))
|
(td :class "py-2 px-3" "Grep within a page or spec"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "py-2 px-3 font-mono text-violet-700" "!raw")
|
(td :class "py-2 px-3 font-mono text-violet-700" "!raw")
|
||||||
(td :class "py-2 px-3 font-mono text-sm" "/(!raw.(~some-component))")
|
(td :class "py-2 px-3 font-mono text-sm" "/sx/(!raw.(~some-component))")
|
||||||
(td :class "py-2 px-3" "Skip layout wrapping — raw content"))
|
(td :class "py-2 px-3" "Skip layout wrapping — raw content"))
|
||||||
(tr :class "border-b border-stone-100"
|
(tr :class "border-b border-stone-100"
|
||||||
(td :class "py-2 px-3 font-mono text-violet-700" "!json")
|
(td :class "py-2 px-3 font-mono text-violet-700" "!json")
|
||||||
(td :class "py-2 px-3 font-mono text-sm" "/(!json.(language.(doc.primitives)))")
|
(td :class "py-2 px-3 font-mono text-sm" "/sx/(!json.(language.(doc.primitives)))")
|
||||||
(td :class "py-2 px-3" "Return data as JSON")))))
|
(td :class "py-2 px-3" "Return data as JSON")))))
|
||||||
|
|
||||||
(p "SX has four sigils, each marking a different kind of name:")
|
(p "SX has four sigils, each marking a different kind of name:")
|
||||||
@@ -224,21 +224,21 @@
|
|||||||
(~doc-subsection :title "Links"
|
(~doc-subsection :title "Links"
|
||||||
(p "Standard anchor tags with SX URL hrefs:")
|
(p "Standard anchor tags with SX URL hrefs:")
|
||||||
(~doc-code :code (highlight
|
(~doc-code :code (highlight
|
||||||
";; Absolute links (the site navigation uses these):\n(a :href \"/(language.(doc.introduction))\" \"Introduction\")\n(a :href \"/(etc.(essay.sx-sucks))\" \"SX Sucks\")\n\n;; Relative links (navigate from current page):\n(a :href \"..inline-edit\" \"Inline Edit\") ;; up and over\n(a :href \".getting-started\" \"Getting Started\") ;; same level\n(a :href \"..\" \"Back\") ;; up one level\n\n;; Direct component links:\n(a :href \"/(~essay-self-defining-medium)\" \"The True Hypermedium\")"
|
";; Absolute links (the site navigation uses these):\n(a :href \"/sx/(language.(doc.introduction))\" \"Introduction\")\n(a :href \"/sx/(etc.(essay.sx-sucks))\" \"SX Sucks\")\n\n;; Relative links (navigate from current page):\n(a :href \"..inline-edit\" \"Inline Edit\") ;; up and over\n(a :href \".getting-started\" \"Getting Started\") ;; same level\n(a :href \"..\" \"Back\") ;; up one level\n\n;; Direct component links:\n(a :href \"/sx/(~essay-self-defining-medium)\" \"The True Hypermedium\")"
|
||||||
"lisp")))
|
"lisp")))
|
||||||
|
|
||||||
(~doc-subsection :title "sx-get — HTMX-style fetching"
|
(~doc-subsection :title "sx-get — HTMX-style fetching"
|
||||||
(p (code "sx-get") " fetches content from an SX URL and swaps it into the DOM. "
|
(p (code "sx-get") " fetches content from an SX URL and swaps it into the DOM. "
|
||||||
"SX URLs work exactly like path URLs:")
|
"SX URLs work exactly like path URLs:")
|
||||||
(~doc-code :code (highlight
|
(~doc-code :code (highlight
|
||||||
";; Fetch and swap a page section:\n(div :sx-get \"/(geography.(hypermedia.(example.progress-bar)))\"\n :sx-trigger \"click\"\n :sx-target \"#content\"\n :sx-swap \"innerHTML\"\n \"Load Progress Bar Example\")\n\n;; Relative sx-get — resolve relative to the current page:\n(div :sx-get \"..inline-edit\"\n :sx-trigger \"click\"\n :sx-target \"#content\"\n :sx-swap \"innerHTML\"\n \"Load Inline Edit\")\n\n;; Paginated content with keyword deltas:\n(button :sx-get \".:page.+1\"\n :sx-trigger \"click\"\n :sx-target \"#results\"\n :sx-swap \"innerHTML\"\n \"Next Page\")"
|
";; Fetch and swap a page section:\n(div :sx-get \"/sx/(geography.(hypermedia.(example.progress-bar)))\"\n :sx-trigger \"click\"\n :sx-target \"#content\"\n :sx-swap \"innerHTML\"\n \"Load Progress Bar Example\")\n\n;; Relative sx-get — resolve relative to the current page:\n(div :sx-get \"..inline-edit\"\n :sx-trigger \"click\"\n :sx-target \"#content\"\n :sx-swap \"innerHTML\"\n \"Load Inline Edit\")\n\n;; Paginated content with keyword deltas:\n(button :sx-get \".:page.+1\"\n :sx-trigger \"click\"\n :sx-target \"#results\"\n :sx-swap \"innerHTML\"\n \"Next Page\")"
|
||||||
"lisp")))
|
"lisp")))
|
||||||
|
|
||||||
(~doc-subsection :title "sx-boost — progressive enhancement"
|
(~doc-subsection :title "sx-boost — progressive enhancement"
|
||||||
(p (code "sx-boost") " upgrades regular links to use HTMX-style fetch+swap "
|
(p (code "sx-boost") " upgrades regular links to use HTMX-style fetch+swap "
|
||||||
"instead of full-page navigation:")
|
"instead of full-page navigation:")
|
||||||
(~doc-code :code (highlight
|
(~doc-code :code (highlight
|
||||||
";; A navigation list with boosted links:\n(nav :sx-boost \"true\"\n (ul\n (li (a :href \"/(language.(doc.introduction))\" \"Introduction\"))\n (li (a :href \"/(language.(doc.components))\" \"Components\"))\n (li (a :href \"/(language.(doc.evaluator))\" \"Evaluator\"))))\n\n;; Clicking any link fetches content via SX URL and swaps,\n;; rather than triggering a full page load.\n;; URL bar updates. Back button works. No JavaScript needed."
|
";; A navigation list with boosted links:\n(nav :sx-boost \"true\"\n (ul\n (li (a :href \"/sx/(language.(doc.introduction))\" \"Introduction\"))\n (li (a :href \"/sx/(language.(doc.components))\" \"Components\"))\n (li (a :href \"/sx/(language.(doc.evaluator))\" \"Evaluator\"))))\n\n;; Clicking any link fetches content via SX URL and swaps,\n;; rather than triggering a full page load.\n;; URL bar updates. Back button works. No JavaScript needed."
|
||||||
"lisp")))
|
"lisp")))
|
||||||
|
|
||||||
(~doc-subsection :title "Pagination pattern"
|
(~doc-subsection :title "Pagination pattern"
|
||||||
@@ -265,7 +265,7 @@
|
|||||||
(~doc-subsection :title "Step 1: Parse"
|
(~doc-subsection :title "Step 1: Parse"
|
||||||
(p "Strip the leading " (code "/") ", replace dots with spaces, parse as SX:")
|
(p "Strip the leading " (code "/") ", replace dots with spaces, parse as SX:")
|
||||||
(~doc-code :code (highlight
|
(~doc-code :code (highlight
|
||||||
"/(language.(doc.introduction))\n→ strip / → (language.(doc.introduction))\n→ dots to spaces → (language (doc introduction))\n→ parse → [Symbol(\"language\"), [Symbol(\"doc\"), Symbol(\"introduction\")]]"
|
"/sx/(language.(doc.introduction))\n→ strip / → (language.(doc.introduction))\n→ dots to spaces → (language (doc introduction))\n→ parse → [Symbol(\"language\"), [Symbol(\"doc\"), Symbol(\"introduction\")]]"
|
||||||
"lisp")))
|
"lisp")))
|
||||||
|
|
||||||
(~doc-subsection :title "Step 2: Auto-quote"
|
(~doc-subsection :title "Step 2: Auto-quote"
|
||||||
@@ -386,7 +386,7 @@
|
|||||||
"This means:")
|
"This means:")
|
||||||
(ul :class "space-y-1 text-stone-600 list-disc pl-5"
|
(ul :class "space-y-1 text-stone-600 list-disc pl-5"
|
||||||
(li (strong "Cacheable") " — CDNs cache by URL, and these are URLs")
|
(li (strong "Cacheable") " — CDNs cache by URL, and these are URLs")
|
||||||
(li (strong "Bookmarkable") " — save " (code "/(language.(spec.signals))") " in your browser")
|
(li (strong "Bookmarkable") " — save " (code "/sx/(language.(spec.signals))") " in your browser")
|
||||||
(li (strong "Shareable") " — paste it in chat, it works")
|
(li (strong "Shareable") " — paste it in chat, it works")
|
||||||
(li (strong "Indexable") " — crawlers follow " (code "<a href>") " links")
|
(li (strong "Indexable") " — crawlers follow " (code "<a href>") " links")
|
||||||
(li (strong "No client library") " — " (code "curl '/(language.(doc.intro))'") " returns content"))
|
(li (strong "No client library") " — " (code "curl '/(language.(doc.intro))'") " returns content"))
|
||||||
@@ -398,17 +398,17 @@
|
|||||||
;; -----------------------------------------------------------------
|
;; -----------------------------------------------------------------
|
||||||
(~doc-section :title "The Router Spec" :id "spec"
|
(~doc-section :title "The Router Spec" :id "spec"
|
||||||
(p "SX URLs are not a Python or JavaScript feature — they are specified in SX itself. "
|
(p "SX URLs are not a Python or JavaScript feature — they are specified in SX itself. "
|
||||||
"The " (a :href "/(language.(spec.router))" :class "text-violet-600 hover:underline" "router spec")
|
"The " (a :href "/sx/(language.(spec.router))" :class "text-violet-600 hover:underline" "router spec")
|
||||||
" (" (code "router.sx") ") defines all URL parsing, matching, relative resolution, "
|
" (" (code "router.sx") ") defines all URL parsing, matching, relative resolution, "
|
||||||
"and special form detection as pure functions.")
|
"and special form detection as pure functions.")
|
||||||
|
|
||||||
(p "Key functions from the spec:")
|
(p "Key functions from the spec:")
|
||||||
(~doc-code :code (highlight
|
(~doc-code :code (highlight
|
||||||
";; Parse a URL into a typed descriptor:\n(parse-sx-url \"/(language.(doc.intro))\")\n→ {\"type\" \"absolute\" \"raw\" \"/(language.(doc.intro))\"}\n\n(parse-sx-url \"/(!source.(~essay))\")\n→ {\"type\" \"special-form\" \"form\" \"!source\"\n \"inner\" \"(~essay)\" \"raw\" \"/(!source.(~essay))\"}\n\n(parse-sx-url \"/(~essay-sx-sucks)\")\n→ {\"type\" \"direct-component\" \"name\" \"~essay-sx-sucks\"\n \"raw\" \"/(~essay-sx-sucks)\"}\n\n(parse-sx-url \"..eval\")\n→ {\"type\" \"relative\" \"raw\" \"..eval\"}\n\n;; Resolve relative URLs:\n(resolve-relative-url\n \"/(geography.(hypermedia.(example.progress-bar)))\"\n \"..inline-edit\")\n→ \"/(geography.(hypermedia.(example.inline-edit)))\"\n\n;; Keyword delta:\n(resolve-relative-url\n \"/(language.(spec.(explore.signals.:page.3)))\"\n \".:page.+1\")\n→ \"/(language.(spec.(explore.signals.:page.4)))\"\n\n;; Check URL type:\n(relative-sx-url? \"..eval\") → true\n(relative-sx-url? \"(.slug)\") → true\n(relative-sx-url? \"/(foo)\") → false\n\n;; Special form inspection:\n(url-special-form? \"!source\") → true\n(url-special-form? \"inspect\") → false\n(url-special-form-name \"/(!source.(~essay))\") → \"!source\"\n(url-special-form-inner \"/(!source.(~essay))\") → \"(~essay)\""
|
";; Parse a URL into a typed descriptor:\n(parse-sx-url \"/sx/(language.(doc.intro))\")\n→ {\"type\" \"absolute\" \"raw\" \"/sx/(language.(doc.intro))\"}\n\n(parse-sx-url \"/sx/(!source.(~essay))\")\n→ {\"type\" \"special-form\" \"form\" \"!source\"\n \"inner\" \"(~essay)\" \"raw\" \"/sx/(!source.(~essay))\"}\n\n(parse-sx-url \"/sx/(~essay-sx-sucks)\")\n→ {\"type\" \"direct-component\" \"name\" \"~essay-sx-sucks\"\n \"raw\" \"/sx/(~essay-sx-sucks)\"}\n\n(parse-sx-url \"..eval\")\n→ {\"type\" \"relative\" \"raw\" \"..eval\"}\n\n;; Resolve relative URLs:\n(resolve-relative-url\n \"/sx/(geography.(hypermedia.(example.progress-bar)))\"\n \"..inline-edit\")\n→ \"/sx/(geography.(hypermedia.(example.inline-edit)))\"\n\n;; Keyword delta:\n(resolve-relative-url\n \"/sx/(language.(spec.(explore.signals.:page.3)))\"\n \".:page.+1\")\n→ \"/sx/(language.(spec.(explore.signals.:page.4)))\"\n\n;; Check URL type:\n(relative-sx-url? \"..eval\") → true\n(relative-sx-url? \"(.slug)\") → true\n(relative-sx-url? \"/sx/(foo)\") → false\n\n;; Special form inspection:\n(url-special-form? \"!source\") → true\n(url-special-form? \"inspect\") → false\n(url-special-form-name \"/sx/(!source.(~essay))\") → \"!source\"\n(url-special-form-inner \"/sx/(!source.(~essay))\") → \"(~essay)\""
|
||||||
"lisp"))
|
"lisp"))
|
||||||
|
|
||||||
(p "These functions are "
|
(p "These functions are "
|
||||||
(a :href "/(language.(test.router))" :class "text-violet-600 hover:underline" "tested with 115 SX tests")
|
(a :href "/sx/(language.(test.router))" :class "text-violet-600 hover:underline" "tested with 115 SX tests")
|
||||||
" covering every edge case — structural navigation, keyword operations, "
|
" covering every edge case — structural navigation, keyword operations, "
|
||||||
"delta values, special form parsing, and bare-dot shorthand. "
|
"delta values, special form parsing, and bare-dot shorthand. "
|
||||||
"The spec is bootstrapped to both JavaScript (" (code "sx-browser.js") ") and "
|
"The spec is bootstrapped to both JavaScript (" (code "sx-browser.js") ") and "
|
||||||
@@ -434,7 +434,7 @@
|
|||||||
(~doc-section :title "The Site Is a REPL" :id "repl"
|
(~doc-section :title "The Site Is a REPL" :id "repl"
|
||||||
(p "The address bar is the input line. The page is the output.")
|
(p "The address bar is the input line. The page is the output.")
|
||||||
(~doc-code :code (highlight
|
(~doc-code :code (highlight
|
||||||
"/(about) ;; renders the about page\n/(!source.(about)) ;; returns its source code\n\n/(language.(spec.signals)) ;; the signals spec\n/(!inspect.(language.(spec.signals))) ;; its dependency graph\n\n/(~essay-sx-sucks) ;; the essay\n/(!source.(~essay-sx-sucks)) ;; its s-expression source\n\n;; The website IS a REPL. Every page is a function call.\n;; Every function call is a URL. Evaluation is navigation."
|
"/sx/(about) ;; renders the about page\n/(!source.(about)) ;; returns its source code\n\n/(language.(spec.signals)) ;; the signals spec\n/(!inspect.(language.(spec.signals))) ;; its dependency graph\n\n/(~essay-sx-sucks) ;; the essay\n/(!source.(~essay-sx-sucks)) ;; its s-expression source\n\n;; The website IS a REPL. Every page is a function call.\n;; Every function call is a URL. Evaluation is navigation."
|
||||||
"lisp"))
|
"lisp"))
|
||||||
(p "You do not need to explain what SX is. "
|
(p "You do not need to explain what SX is. "
|
||||||
"Show someone a URL, and they immediately understand the philosophy. "
|
"Show someone a URL, and they immediately understand the philosophy. "
|
||||||
|
|||||||
@@ -98,27 +98,27 @@ Per-spec platform functions:
|
|||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(h2 :class "text-2xl font-semibold text-stone-800" "Test specs")
|
(h2 :class "text-2xl font-semibold text-stone-800" "Test specs")
|
||||||
(div :class "grid grid-cols-1 md:grid-cols-2 gap-4"
|
(div :class "grid grid-cols-1 md:grid-cols-2 gap-4"
|
||||||
(a :href "/(language.(test.eval))" :class "block rounded-lg border border-stone-200 p-5 hover:border-violet-300 hover:bg-violet-50 transition-colors"
|
(a :href "/sx/(language.(test.eval))" :class "block rounded-lg border border-stone-200 p-5 hover:border-violet-300 hover:bg-violet-50 transition-colors"
|
||||||
(h3 :class "font-semibold text-stone-800" "Evaluator")
|
(h3 :class "font-semibold text-stone-800" "Evaluator")
|
||||||
(p :class "text-sm text-stone-500" "81 tests — literals, arithmetic, strings, lists, dicts, special forms, lambdas, components, macros")
|
(p :class "text-sm text-stone-500" "81 tests — literals, arithmetic, strings, lists, dicts, special forms, lambdas, components, macros")
|
||||||
(p :class "text-xs text-violet-600 mt-1" "test-eval.sx"))
|
(p :class "text-xs text-violet-600 mt-1" "test-eval.sx"))
|
||||||
(a :href "/(language.(test.parser))" :class "block rounded-lg border border-stone-200 p-5 hover:border-violet-300 hover:bg-violet-50 transition-colors"
|
(a :href "/sx/(language.(test.parser))" :class "block rounded-lg border border-stone-200 p-5 hover:border-violet-300 hover:bg-violet-50 transition-colors"
|
||||||
(h3 :class "font-semibold text-stone-800" "Parser")
|
(h3 :class "font-semibold text-stone-800" "Parser")
|
||||||
(p :class "text-sm text-stone-500" "39 tests — tokenization, parsing, escape sequences, quote sugar, serialization, round-trips")
|
(p :class "text-sm text-stone-500" "39 tests — tokenization, parsing, escape sequences, quote sugar, serialization, round-trips")
|
||||||
(p :class "text-xs text-violet-600 mt-1" "test-parser.sx"))
|
(p :class "text-xs text-violet-600 mt-1" "test-parser.sx"))
|
||||||
(a :href "/(language.(test.router))" :class "block rounded-lg border border-stone-200 p-5 hover:border-violet-300 hover:bg-violet-50 transition-colors"
|
(a :href "/sx/(language.(test.router))" :class "block rounded-lg border border-stone-200 p-5 hover:border-violet-300 hover:bg-violet-50 transition-colors"
|
||||||
(h3 :class "font-semibold text-stone-800" "Router")
|
(h3 :class "font-semibold text-stone-800" "Router")
|
||||||
(p :class "text-sm text-stone-500" "18 tests — path splitting, pattern parsing, segment matching, parameter extraction")
|
(p :class "text-sm text-stone-500" "18 tests — path splitting, pattern parsing, segment matching, parameter extraction")
|
||||||
(p :class "text-xs text-violet-600 mt-1" "test-router.sx"))
|
(p :class "text-xs text-violet-600 mt-1" "test-router.sx"))
|
||||||
(a :href "/(language.(test.render))" :class "block rounded-lg border border-stone-200 p-5 hover:border-violet-300 hover:bg-violet-50 transition-colors"
|
(a :href "/sx/(language.(test.render))" :class "block rounded-lg border border-stone-200 p-5 hover:border-violet-300 hover:bg-violet-50 transition-colors"
|
||||||
(h3 :class "font-semibold text-stone-800" "Renderer")
|
(h3 :class "font-semibold text-stone-800" "Renderer")
|
||||||
(p :class "text-sm text-stone-500" "23 tests — elements, attributes, void elements, fragments, escaping, control flow, components")
|
(p :class "text-sm text-stone-500" "23 tests — elements, attributes, void elements, fragments, escaping, control flow, components")
|
||||||
(p :class "text-xs text-violet-600 mt-1" "test-render.sx"))
|
(p :class "text-xs text-violet-600 mt-1" "test-render.sx"))
|
||||||
(a :href "/(language.(test.deps))" :class "block rounded-lg border border-stone-200 p-5 hover:border-violet-300 hover:bg-violet-50 transition-colors"
|
(a :href "/sx/(language.(test.deps))" :class "block rounded-lg border border-stone-200 p-5 hover:border-violet-300 hover:bg-violet-50 transition-colors"
|
||||||
(h3 :class "font-semibold text-stone-800" "Dependencies")
|
(h3 :class "font-semibold text-stone-800" "Dependencies")
|
||||||
(p :class "text-sm text-stone-500" "33 tests — scan-refs, transitive-deps, components-needed, IO detection, purity classification")
|
(p :class "text-sm text-stone-500" "33 tests — scan-refs, transitive-deps, components-needed, IO detection, purity classification")
|
||||||
(p :class "text-xs text-violet-600 mt-1" "test-deps.sx"))
|
(p :class "text-xs text-violet-600 mt-1" "test-deps.sx"))
|
||||||
(a :href "/(language.(test.engine))" :class "block rounded-lg border border-stone-200 p-5 hover:border-violet-300 hover:bg-violet-50 transition-colors"
|
(a :href "/sx/(language.(test.engine))" :class "block rounded-lg border border-stone-200 p-5 hover:border-violet-300 hover:bg-violet-50 transition-colors"
|
||||||
(h3 :class "font-semibold text-stone-800" "Engine")
|
(h3 :class "font-semibold text-stone-800" "Engine")
|
||||||
(p :class "text-sm text-stone-500" "37 tests — parse-time, trigger specs, swap specs, retry logic, param filtering")
|
(p :class "text-sm text-stone-500" "37 tests — parse-time, trigger specs, swap specs, retry logic, param filtering")
|
||||||
(p :class "text-xs text-violet-600 mt-1" "test-engine.sx"))))
|
(p :class "text-xs text-violet-600 mt-1" "test-engine.sx"))))
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
(div :id "click-result" :class "p-4 rounded bg-stone-100 text-stone-500 text-center"
|
(div :id "click-result" :class "p-4 rounded bg-stone-100 text-stone-500 text-center"
|
||||||
"Click the button to load content.")
|
"Click the button to load content.")
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.click))))"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.click))))"
|
||||||
:sx-target "#click-result"
|
:sx-target "#click-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors"
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors"
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
(defcomp ~form-demo ()
|
(defcomp ~form-demo ()
|
||||||
(div :class "space-y-4"
|
(div :class "space-y-4"
|
||||||
(form
|
(form
|
||||||
:sx-post "/(geography.(hypermedia.(example.(api.form))))"
|
:sx-post "/sx/(geography.(hypermedia.(example.(api.form))))"
|
||||||
:sx-target "#form-result"
|
:sx-target "#form-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "space-y-3"
|
:class "space-y-3"
|
||||||
@@ -63,7 +63,7 @@
|
|||||||
(defcomp ~polling-demo ()
|
(defcomp ~polling-demo ()
|
||||||
(div :class "space-y-4"
|
(div :class "space-y-4"
|
||||||
(div :id "poll-target"
|
(div :id "poll-target"
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.poll))))"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.poll))))"
|
||||||
:sx-trigger "load, every 2s"
|
:sx-trigger "load, every 2s"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "p-4 rounded border border-stone-200 bg-stone-100 text-center font-mono"
|
:class "p-4 rounded border border-stone-200 bg-stone-100 text-center font-mono"
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
(td :class "px-3 py-2 text-stone-700" name)
|
(td :class "px-3 py-2 text-stone-700" name)
|
||||||
(td :class "px-3 py-2"
|
(td :class "px-3 py-2"
|
||||||
(button
|
(button
|
||||||
:sx-delete (str "/(geography.(hypermedia.(example.(api.(delete." id ")))))")
|
:sx-delete (str "/sx/(geography.(hypermedia.(example.(api.(delete." id ")))))")
|
||||||
:sx-target (str "#row-" id)
|
:sx-target (str "#row-" id)
|
||||||
:sx-swap "outerHTML"
|
:sx-swap "outerHTML"
|
||||||
:sx-confirm "Delete this item?"
|
:sx-confirm "Delete this item?"
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
(div :class "flex items-center justify-between p-3 rounded border border-stone-200"
|
(div :class "flex items-center justify-between p-3 rounded border border-stone-200"
|
||||||
(span :class "text-stone-800" value)
|
(span :class "text-stone-800" value)
|
||||||
(button
|
(button
|
||||||
:sx-get (str "/(geography.(hypermedia.(example.(api.edit))))?value=" value)
|
:sx-get (str "/sx/(geography.(hypermedia.(example.(api.edit))))?value=" value)
|
||||||
:sx-target "#edit-target"
|
:sx-target "#edit-target"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "text-sm text-violet-600 hover:text-violet-800"
|
:class "text-sm text-violet-600 hover:text-violet-800"
|
||||||
@@ -124,7 +124,7 @@
|
|||||||
|
|
||||||
(defcomp ~inline-edit-form (&key value)
|
(defcomp ~inline-edit-form (&key value)
|
||||||
(form
|
(form
|
||||||
:sx-post "/(geography.(hypermedia.(example.(api.edit))))"
|
:sx-post "/sx/(geography.(hypermedia.(example.(api.edit))))"
|
||||||
:sx-target "#edit-target"
|
:sx-target "#edit-target"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "flex items-center gap-2 p-3 rounded border border-violet-300 bg-violet-50"
|
:class "flex items-center gap-2 p-3 rounded border border-violet-300 bg-violet-50"
|
||||||
@@ -134,7 +134,7 @@
|
|||||||
:class "px-3 py-1.5 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
:class "px-3 py-1.5 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
||||||
"save")
|
"save")
|
||||||
(button :type "button"
|
(button :type "button"
|
||||||
:sx-get (str "/(geography.(hypermedia.(example.(api.edit-cancel))))?value=" value)
|
:sx-get (str "/sx/(geography.(hypermedia.(example.(api.edit-cancel))))?value=" value)
|
||||||
:sx-target "#edit-target"
|
:sx-target "#edit-target"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "px-3 py-1.5 bg-stone-200 text-stone-700 rounded text-sm hover:bg-stone-300"
|
:class "px-3 py-1.5 bg-stone-200 text-stone-700 rounded text-sm hover:bg-stone-300"
|
||||||
@@ -152,7 +152,7 @@
|
|||||||
(p :class "text-stone-500" "Box B")
|
(p :class "text-stone-500" "Box B")
|
||||||
(p :class "text-sm text-stone-400" "Waiting...")))
|
(p :class "text-sm text-stone-400" "Waiting...")))
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.oob))))"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.oob))))"
|
||||||
:sx-target "#oob-box-a"
|
:sx-target "#oob-box-a"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
||||||
@@ -164,7 +164,7 @@
|
|||||||
(div :class "space-y-4"
|
(div :class "space-y-4"
|
||||||
(p :class "text-sm text-stone-500" "The content below loads automatically when the page renders.")
|
(p :class "text-sm text-stone-500" "The content below loads automatically when the page renders.")
|
||||||
(div :id "lazy-target"
|
(div :id "lazy-target"
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.lazy))))"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.lazy))))"
|
||||||
:sx-trigger "load"
|
:sx-trigger "load"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "p-6 rounded border border-stone-200 bg-stone-100 text-center"
|
:class "p-6 rounded border border-stone-200 bg-stone-100 text-center"
|
||||||
@@ -187,7 +187,7 @@
|
|||||||
(str "Item " (+ i 1) " — loaded with the page")))
|
(str "Item " (+ i 1) " — loaded with the page")))
|
||||||
(list 1 2 3 4 5))
|
(list 1 2 3 4 5))
|
||||||
(div :id "scroll-sentinel"
|
(div :id "scroll-sentinel"
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.scroll))))?page=2"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.scroll))))?page=2"
|
||||||
:sx-trigger "intersect once"
|
:sx-trigger "intersect once"
|
||||||
:sx-target "#scroll-items"
|
:sx-target "#scroll-items"
|
||||||
:sx-swap "beforeend"
|
:sx-swap "beforeend"
|
||||||
@@ -201,7 +201,7 @@
|
|||||||
items)
|
items)
|
||||||
(when (<= page 5)
|
(when (<= page 5)
|
||||||
(div :id "scroll-sentinel"
|
(div :id "scroll-sentinel"
|
||||||
:sx-get (str "/(geography.(hypermedia.(example.(api.scroll))))?page=" page)
|
:sx-get (str "/sx/(geography.(hypermedia.(example.(api.scroll))))?page=" page)
|
||||||
:sx-trigger "intersect once"
|
:sx-trigger "intersect once"
|
||||||
:sx-target "#scroll-items"
|
:sx-target "#scroll-items"
|
||||||
:sx-swap "beforeend"
|
:sx-swap "beforeend"
|
||||||
@@ -217,7 +217,7 @@
|
|||||||
(div :class "bg-violet-600 h-4 rounded-full transition-all" :style "width: 0%"))
|
(div :class "bg-violet-600 h-4 rounded-full transition-all" :style "width: 0%"))
|
||||||
(p :class "text-sm text-stone-500 text-center" "Click start to begin."))
|
(p :class "text-sm text-stone-500 text-center" "Click start to begin."))
|
||||||
(button
|
(button
|
||||||
:sx-post "/(geography.(hypermedia.(example.(api.progress-start))))"
|
:sx-post "/sx/(geography.(hypermedia.(example.(api.progress-start))))"
|
||||||
:sx-target "#progress-target"
|
:sx-target "#progress-target"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
||||||
@@ -230,7 +230,7 @@
|
|||||||
:style (str "width: " percent "%")))
|
:style (str "width: " percent "%")))
|
||||||
(p :class "text-sm text-stone-500 text-center" (str percent "% complete"))
|
(p :class "text-sm text-stone-500 text-center" (str percent "% complete"))
|
||||||
(when (< percent 100)
|
(when (< percent 100)
|
||||||
(div :sx-get (str "/(geography.(hypermedia.(example.(api.progress-status))))?job=" job-id)
|
(div :sx-get (str "/sx/(geography.(hypermedia.(example.(api.progress-status))))?job=" job-id)
|
||||||
:sx-trigger "load delay:500ms"
|
:sx-trigger "load delay:500ms"
|
||||||
:sx-target "#progress-target"
|
:sx-target "#progress-target"
|
||||||
:sx-swap "innerHTML"))
|
:sx-swap "innerHTML"))
|
||||||
@@ -242,7 +242,7 @@
|
|||||||
(defcomp ~active-search-demo ()
|
(defcomp ~active-search-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(input :type "text" :name "q"
|
(input :type "text" :name "q"
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.search))))"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.search))))"
|
||||||
:sx-trigger "keyup delay:300ms changed"
|
:sx-trigger "keyup delay:300ms changed"
|
||||||
:sx-target "#search-results"
|
:sx-target "#search-results"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
@@ -262,11 +262,11 @@
|
|||||||
;; --- Inline validation demo ---
|
;; --- Inline validation demo ---
|
||||||
|
|
||||||
(defcomp ~inline-validation-demo ()
|
(defcomp ~inline-validation-demo ()
|
||||||
(form :class "space-y-4" :sx-post "/(geography.(hypermedia.(example.(api.validate-submit))))" :sx-target "#validation-result" :sx-swap "innerHTML"
|
(form :class "space-y-4" :sx-post "/sx/(geography.(hypermedia.(example.(api.validate-submit))))" :sx-target "#validation-result" :sx-swap "innerHTML"
|
||||||
(div
|
(div
|
||||||
(label :class "block text-sm font-medium text-stone-700 mb-1" "Email")
|
(label :class "block text-sm font-medium text-stone-700 mb-1" "Email")
|
||||||
(input :type "text" :name "email" :placeholder "user@example.com"
|
(input :type "text" :name "email" :placeholder "user@example.com"
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.validate))))"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.validate))))"
|
||||||
:sx-trigger "blur"
|
:sx-trigger "blur"
|
||||||
:sx-target "#email-feedback"
|
:sx-target "#email-feedback"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
@@ -290,7 +290,7 @@
|
|||||||
(div
|
(div
|
||||||
(label :class "block text-sm font-medium text-stone-700 mb-1" "Category")
|
(label :class "block text-sm font-medium text-stone-700 mb-1" "Category")
|
||||||
(select :name "category"
|
(select :name "category"
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.values))))"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.values))))"
|
||||||
:sx-trigger "change"
|
:sx-trigger "change"
|
||||||
:sx-target "#value-items"
|
:sx-target "#value-items"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
@@ -314,7 +314,7 @@
|
|||||||
(defcomp ~reset-on-submit-demo ()
|
(defcomp ~reset-on-submit-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(form :id "reset-form"
|
(form :id "reset-form"
|
||||||
:sx-post "/(geography.(hypermedia.(example.(api.reset-submit))))"
|
:sx-post "/sx/(geography.(hypermedia.(example.(api.reset-submit))))"
|
||||||
:sx-target "#reset-result"
|
:sx-target "#reset-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-on:afterSwap "this.reset()"
|
:sx-on:afterSwap "this.reset()"
|
||||||
@@ -354,7 +354,7 @@
|
|||||||
(td :class "px-3 py-2 text-stone-700" stock)
|
(td :class "px-3 py-2 text-stone-700" stock)
|
||||||
(td :class "px-3 py-2"
|
(td :class "px-3 py-2"
|
||||||
(button
|
(button
|
||||||
:sx-get (str "/(geography.(hypermedia.(example.(api.(editrow." id ")))))")
|
:sx-get (str "/sx/(geography.(hypermedia.(example.(api.(editrow." id ")))))")
|
||||||
:sx-target (str "#erow-" id)
|
:sx-target (str "#erow-" id)
|
||||||
:sx-swap "outerHTML"
|
:sx-swap "outerHTML"
|
||||||
:class "text-sm text-violet-600 hover:text-violet-800"
|
:class "text-sm text-violet-600 hover:text-violet-800"
|
||||||
@@ -373,14 +373,14 @@
|
|||||||
:class "w-20 px-2 py-1 border border-stone-300 rounded text-sm"))
|
:class "w-20 px-2 py-1 border border-stone-300 rounded text-sm"))
|
||||||
(td :class "px-3 py-2 space-x-1"
|
(td :class "px-3 py-2 space-x-1"
|
||||||
(button
|
(button
|
||||||
:sx-post (str "/(geography.(hypermedia.(example.(api.(editrow." id ")))))")
|
:sx-post (str "/sx/(geography.(hypermedia.(example.(api.(editrow." id ")))))")
|
||||||
:sx-target (str "#erow-" id)
|
:sx-target (str "#erow-" id)
|
||||||
:sx-swap "outerHTML"
|
:sx-swap "outerHTML"
|
||||||
:sx-include (str "#erow-" id)
|
:sx-include (str "#erow-" id)
|
||||||
:class "text-sm text-emerald-600 hover:text-emerald-800"
|
:class "text-sm text-emerald-600 hover:text-emerald-800"
|
||||||
"save")
|
"save")
|
||||||
(button
|
(button
|
||||||
:sx-get (str "/(geography.(hypermedia.(example.(api.(editrow-cancel." id ")))))")
|
:sx-get (str "/sx/(geography.(hypermedia.(example.(api.(editrow-cancel." id ")))))")
|
||||||
:sx-target (str "#erow-" id)
|
:sx-target (str "#erow-" id)
|
||||||
:sx-swap "outerHTML"
|
:sx-swap "outerHTML"
|
||||||
:class "text-sm text-stone-500 hover:text-stone-700"
|
:class "text-sm text-stone-500 hover:text-stone-700"
|
||||||
@@ -393,14 +393,14 @@
|
|||||||
(form :id "bulk-form"
|
(form :id "bulk-form"
|
||||||
(div :class "flex gap-2 mb-3"
|
(div :class "flex gap-2 mb-3"
|
||||||
(button :type "button"
|
(button :type "button"
|
||||||
:sx-post "/(geography.(hypermedia.(example.(api.bulk))))?action=activate"
|
:sx-post "/sx/(geography.(hypermedia.(example.(api.bulk))))?action=activate"
|
||||||
:sx-target "#bulk-table"
|
:sx-target "#bulk-table"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-include "#bulk-form"
|
:sx-include "#bulk-form"
|
||||||
:class "px-3 py-1.5 bg-emerald-600 text-white rounded text-sm hover:bg-emerald-700"
|
:class "px-3 py-1.5 bg-emerald-600 text-white rounded text-sm hover:bg-emerald-700"
|
||||||
"Activate")
|
"Activate")
|
||||||
(button :type "button"
|
(button :type "button"
|
||||||
:sx-post "/(geography.(hypermedia.(example.(api.bulk))))?action=deactivate"
|
:sx-post "/sx/(geography.(hypermedia.(example.(api.bulk))))?action=deactivate"
|
||||||
:sx-target "#bulk-table"
|
:sx-target "#bulk-table"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-include "#bulk-form"
|
:sx-include "#bulk-form"
|
||||||
@@ -437,19 +437,19 @@
|
|||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(div :class "flex gap-2"
|
(div :class "flex gap-2"
|
||||||
(button
|
(button
|
||||||
:sx-post "/(geography.(hypermedia.(example.(api.swap-log))))?mode=beforeend"
|
:sx-post "/sx/(geography.(hypermedia.(example.(api.swap-log))))?mode=beforeend"
|
||||||
:sx-target "#swap-log"
|
:sx-target "#swap-log"
|
||||||
:sx-swap "beforeend"
|
:sx-swap "beforeend"
|
||||||
:class "px-3 py-1.5 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
:class "px-3 py-1.5 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
||||||
"Add to End")
|
"Add to End")
|
||||||
(button
|
(button
|
||||||
:sx-post "/(geography.(hypermedia.(example.(api.swap-log))))?mode=afterbegin"
|
:sx-post "/sx/(geography.(hypermedia.(example.(api.swap-log))))?mode=afterbegin"
|
||||||
:sx-target "#swap-log"
|
:sx-target "#swap-log"
|
||||||
:sx-swap "afterbegin"
|
:sx-swap "afterbegin"
|
||||||
:class "px-3 py-1.5 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
:class "px-3 py-1.5 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
||||||
"Add to Start")
|
"Add to Start")
|
||||||
(button
|
(button
|
||||||
:sx-post "/(geography.(hypermedia.(example.(api.swap-log))))?mode=none"
|
:sx-post "/sx/(geography.(hypermedia.(example.(api.swap-log))))?mode=none"
|
||||||
:sx-target "#swap-log"
|
:sx-target "#swap-log"
|
||||||
:sx-swap "none"
|
:sx-swap "none"
|
||||||
:class "px-3 py-1.5 bg-stone-600 text-white rounded text-sm hover:bg-stone-700"
|
:class "px-3 py-1.5 bg-stone-600 text-white rounded text-sm hover:bg-stone-700"
|
||||||
@@ -469,21 +469,21 @@
|
|||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(div :class "flex gap-2"
|
(div :class "flex gap-2"
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.dashboard))))"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.dashboard))))"
|
||||||
:sx-target "#filter-target"
|
:sx-target "#filter-target"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-select "#dash-stats"
|
:sx-select "#dash-stats"
|
||||||
:class "px-3 py-1.5 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
:class "px-3 py-1.5 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
||||||
"Stats Only")
|
"Stats Only")
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.dashboard))))"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.dashboard))))"
|
||||||
:sx-target "#filter-target"
|
:sx-target "#filter-target"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-select "#dash-header"
|
:sx-select "#dash-header"
|
||||||
:class "px-3 py-1.5 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
:class "px-3 py-1.5 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
||||||
"Header Only")
|
"Header Only")
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.dashboard))))"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.dashboard))))"
|
||||||
:sx-target "#filter-target"
|
:sx-target "#filter-target"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "px-3 py-1.5 bg-stone-600 text-white rounded text-sm hover:bg-stone-700"
|
:class "px-3 py-1.5 bg-stone-600 text-white rounded text-sm hover:bg-stone-700"
|
||||||
@@ -505,10 +505,10 @@
|
|||||||
|
|
||||||
(defcomp ~tab-btn (&key tab label active)
|
(defcomp ~tab-btn (&key tab label active)
|
||||||
(button
|
(button
|
||||||
:sx-get (str "/(geography.(hypermedia.(example.(api.(tabs." tab ")))))")
|
:sx-get (str "/sx/(geography.(hypermedia.(example.(api.(tabs." tab ")))))")
|
||||||
:sx-target "#tab-content"
|
:sx-target "#tab-content"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-push-url (str "/(geography.(hypermedia.(example.tabs)))?tab=" tab)
|
:sx-push-url (str "/sx/(geography.(hypermedia.(example.tabs)))?tab=" tab)
|
||||||
:class (str "px-4 py-2 text-sm font-medium border-b-2 -mb-px transition-colors "
|
:class (str "px-4 py-2 text-sm font-medium border-b-2 -mb-px transition-colors "
|
||||||
(if (= active "true")
|
(if (= active "true")
|
||||||
"border-violet-600 text-violet-600"
|
"border-violet-600 text-violet-600"
|
||||||
@@ -520,7 +520,7 @@
|
|||||||
(defcomp ~animations-demo ()
|
(defcomp ~animations-demo ()
|
||||||
(div :class "space-y-4"
|
(div :class "space-y-4"
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.animate))))"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.animate))))"
|
||||||
:sx-target "#anim-target"
|
:sx-target "#anim-target"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
||||||
@@ -539,7 +539,7 @@
|
|||||||
(defcomp ~dialogs-demo ()
|
(defcomp ~dialogs-demo ()
|
||||||
(div
|
(div
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.dialog))))"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.dialog))))"
|
||||||
:sx-target "#dialog-container"
|
:sx-target "#dialog-container"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
||||||
@@ -549,7 +549,7 @@
|
|||||||
(defcomp ~dialog-modal (&key title message)
|
(defcomp ~dialog-modal (&key title message)
|
||||||
(div :class "fixed inset-0 z-50 flex items-center justify-center"
|
(div :class "fixed inset-0 z-50 flex items-center justify-center"
|
||||||
(div :class "absolute inset-0 bg-black/50"
|
(div :class "absolute inset-0 bg-black/50"
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.dialog-close))))"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.dialog-close))))"
|
||||||
:sx-target "#dialog-container"
|
:sx-target "#dialog-container"
|
||||||
:sx-swap "innerHTML")
|
:sx-swap "innerHTML")
|
||||||
(div :class "relative bg-stone-100 rounded-lg shadow-xl p-6 max-w-md w-full mx-4 space-y-4"
|
(div :class "relative bg-stone-100 rounded-lg shadow-xl p-6 max-w-md w-full mx-4 space-y-4"
|
||||||
@@ -557,13 +557,13 @@
|
|||||||
(p :class "text-stone-600" message)
|
(p :class "text-stone-600" message)
|
||||||
(div :class "flex justify-end gap-2"
|
(div :class "flex justify-end gap-2"
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.dialog-close))))"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.dialog-close))))"
|
||||||
:sx-target "#dialog-container"
|
:sx-target "#dialog-container"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "px-4 py-2 bg-stone-200 text-stone-700 rounded text-sm hover:bg-stone-300"
|
:class "px-4 py-2 bg-stone-200 text-stone-700 rounded text-sm hover:bg-stone-300"
|
||||||
"Cancel")
|
"Cancel")
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.dialog-close))))"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.dialog-close))))"
|
||||||
:sx-target "#dialog-container"
|
:sx-target "#dialog-container"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "px-4 py-2 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
:class "px-4 py-2 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
||||||
@@ -586,16 +586,16 @@
|
|||||||
(kbd :class "px-2 py-0.5 bg-stone-100 border border-stone-300 rounded text-xs font-mono" "h")
|
(kbd :class "px-2 py-0.5 bg-stone-100 border border-stone-300 rounded text-xs font-mono" "h")
|
||||||
(span :class "text-sm text-stone-500" "Help"))))
|
(span :class "text-sm text-stone-500" "Help"))))
|
||||||
(div :id "kbd-target"
|
(div :id "kbd-target"
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.keyboard))))?key=s"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.keyboard))))?key=s"
|
||||||
:sx-trigger "keyup[key=='s'&&!event.target.matches('input,textarea')] from:body"
|
:sx-trigger "keyup[key=='s'&&!event.target.matches('input,textarea')] from:body"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "p-4 rounded border border-stone-200 bg-stone-100 text-center"
|
:class "p-4 rounded border border-stone-200 bg-stone-100 text-center"
|
||||||
(p :class "text-stone-400 text-sm" "Press a shortcut key..."))
|
(p :class "text-stone-400 text-sm" "Press a shortcut key..."))
|
||||||
(div :sx-get "/(geography.(hypermedia.(example.(api.keyboard))))?key=n"
|
(div :sx-get "/sx/(geography.(hypermedia.(example.(api.keyboard))))?key=n"
|
||||||
:sx-trigger "keyup[key=='n'&&!event.target.matches('input,textarea')] from:body"
|
:sx-trigger "keyup[key=='n'&&!event.target.matches('input,textarea')] from:body"
|
||||||
:sx-target "#kbd-target"
|
:sx-target "#kbd-target"
|
||||||
:sx-swap "innerHTML")
|
:sx-swap "innerHTML")
|
||||||
(div :sx-get "/(geography.(hypermedia.(example.(api.keyboard))))?key=h"
|
(div :sx-get "/sx/(geography.(hypermedia.(example.(api.keyboard))))?key=h"
|
||||||
:sx-trigger "keyup[key=='h'&&!event.target.matches('input,textarea')] from:body"
|
:sx-trigger "keyup[key=='h'&&!event.target.matches('input,textarea')] from:body"
|
||||||
:sx-target "#kbd-target"
|
:sx-target "#kbd-target"
|
||||||
:sx-swap "innerHTML")))
|
:sx-swap "innerHTML")))
|
||||||
@@ -619,7 +619,7 @@
|
|||||||
(p :class "text-sm text-stone-500" email)
|
(p :class "text-sm text-stone-500" email)
|
||||||
(p :class "text-sm text-stone-500" role))
|
(p :class "text-sm text-stone-500" role))
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.putpatch-edit-all))))"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.putpatch-edit-all))))"
|
||||||
:sx-target "#pp-target"
|
:sx-target "#pp-target"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "text-sm text-violet-600 hover:text-violet-800"
|
:class "text-sm text-violet-600 hover:text-violet-800"
|
||||||
@@ -627,7 +627,7 @@
|
|||||||
|
|
||||||
(defcomp ~pp-form-full (&key name email role)
|
(defcomp ~pp-form-full (&key name email role)
|
||||||
(form
|
(form
|
||||||
:sx-put "/(geography.(hypermedia.(example.(api.putpatch))))"
|
:sx-put "/sx/(geography.(hypermedia.(example.(api.putpatch))))"
|
||||||
:sx-target "#pp-target"
|
:sx-target "#pp-target"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "space-y-3"
|
:class "space-y-3"
|
||||||
@@ -648,7 +648,7 @@
|
|||||||
:class "px-4 py-2 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
:class "px-4 py-2 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
||||||
"Save All (PUT)")
|
"Save All (PUT)")
|
||||||
(button :type "button"
|
(button :type "button"
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.putpatch-cancel))))"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.putpatch-cancel))))"
|
||||||
:sx-target "#pp-target"
|
:sx-target "#pp-target"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "px-4 py-2 bg-stone-200 text-stone-700 rounded text-sm hover:bg-stone-300"
|
:class "px-4 py-2 bg-stone-200 text-stone-700 rounded text-sm hover:bg-stone-300"
|
||||||
@@ -659,7 +659,7 @@
|
|||||||
(defcomp ~json-encoding-demo ()
|
(defcomp ~json-encoding-demo ()
|
||||||
(div :class "space-y-4"
|
(div :class "space-y-4"
|
||||||
(form
|
(form
|
||||||
:sx-post "/(geography.(hypermedia.(example.(api.json-echo))))"
|
:sx-post "/sx/(geography.(hypermedia.(example.(api.json-echo))))"
|
||||||
:sx-target "#json-result"
|
:sx-target "#json-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-encoding "json"
|
:sx-encoding "json"
|
||||||
@@ -691,7 +691,7 @@
|
|||||||
(div :class "space-y-2"
|
(div :class "space-y-2"
|
||||||
(h4 :class "text-sm font-semibold text-stone-700" "sx-vals — send extra values")
|
(h4 :class "text-sm font-semibold text-stone-700" "sx-vals — send extra values")
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.echo-vals))))"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.echo-vals))))"
|
||||||
:sx-target "#vals-result"
|
:sx-target "#vals-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-vals "{\"source\": \"button\", \"version\": \"2.0\"}"
|
:sx-vals "{\"source\": \"button\", \"version\": \"2.0\"}"
|
||||||
@@ -702,7 +702,7 @@
|
|||||||
(div :class "space-y-2"
|
(div :class "space-y-2"
|
||||||
(h4 :class "text-sm font-semibold text-stone-700" "sx-headers — send custom headers")
|
(h4 :class "text-sm font-semibold text-stone-700" "sx-headers — send custom headers")
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.echo-headers))))"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.echo-headers))))"
|
||||||
:sx-target "#headers-result"
|
:sx-target "#headers-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-headers {:X-Custom-Token "abc123" :X-Request-Source "demo"}
|
:sx-headers {:X-Custom-Token "abc123" :X-Request-Source "demo"}
|
||||||
@@ -723,7 +723,7 @@
|
|||||||
(defcomp ~loading-states-demo ()
|
(defcomp ~loading-states-demo ()
|
||||||
(div :class "space-y-4"
|
(div :class "space-y-4"
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.slow))))"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.slow))))"
|
||||||
:sx-target "#loading-result"
|
:sx-target "#loading-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "sx-loading-btn px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm flex items-center gap-2"
|
:class "sx-loading-btn px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm flex items-center gap-2"
|
||||||
@@ -742,7 +742,7 @@
|
|||||||
(defcomp ~sync-replace-demo ()
|
(defcomp ~sync-replace-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(input :type "text" :name "q"
|
(input :type "text" :name "q"
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.slow-search))))"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.slow-search))))"
|
||||||
:sx-trigger "keyup delay:200ms changed"
|
:sx-trigger "keyup delay:200ms changed"
|
||||||
:sx-target "#sync-result"
|
:sx-target "#sync-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
@@ -762,7 +762,7 @@
|
|||||||
(defcomp ~retry-demo ()
|
(defcomp ~retry-demo ()
|
||||||
(div :class "space-y-4"
|
(div :class "space-y-4"
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(example.(api.flaky))))"
|
:sx-get "/sx/(geography.(hypermedia.(example.(api.flaky))))"
|
||||||
:sx-target "#retry-result"
|
:sx-target "#retry-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-retry "exponential:1000:8000"
|
:sx-retry "exponential:1000:8000"
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
:path "/language/"
|
:path "/language/"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path "/(language)"))
|
:content (~sx-doc :path "/sx/(language)"))
|
||||||
|
|
||||||
;; ---------------------------------------------------------------------------
|
;; ---------------------------------------------------------------------------
|
||||||
;; Docs section (under Language)
|
;; Docs section (under Language)
|
||||||
@@ -31,13 +31,13 @@
|
|||||||
:path "/language/docs/"
|
:path "/language/docs/"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path "/(language.(doc))" (~docs-introduction-content)))
|
:content (~sx-doc :path "/sx/(language.(doc))" (~docs-introduction-content)))
|
||||||
|
|
||||||
(defpage docs-page
|
(defpage docs-page
|
||||||
:path "/language/docs/<slug>"
|
:path "/language/docs/<slug>"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path (str "/(language.(doc." slug "))")
|
:content (~sx-doc :path (str "/sx/(language.(doc." slug "))")
|
||||||
(case slug
|
(case slug
|
||||||
"introduction" (~docs-introduction-content)
|
"introduction" (~docs-introduction-content)
|
||||||
"getting-started" (~docs-getting-started-content)
|
"getting-started" (~docs-getting-started-content)
|
||||||
@@ -58,20 +58,20 @@
|
|||||||
:path "/geography/hypermedia/"
|
:path "/geography/hypermedia/"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path "/(geography.(hypermedia))"))
|
:content (~sx-doc :path "/sx/(geography.(hypermedia))"))
|
||||||
|
|
||||||
(defpage reference-index
|
(defpage reference-index
|
||||||
:path "/geography/hypermedia/reference/"
|
:path "/geography/hypermedia/reference/"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path "/(geography.(hypermedia.(reference)))" (~reference-index-content)))
|
:content (~sx-doc :path "/sx/(geography.(hypermedia.(reference)))" (~reference-index-content)))
|
||||||
|
|
||||||
(defpage reference-page
|
(defpage reference-page
|
||||||
:path "/geography/hypermedia/reference/<slug>"
|
:path "/geography/hypermedia/reference/<slug>"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:data (reference-data slug)
|
:data (reference-data slug)
|
||||||
:content (~sx-doc :path (str "/(geography.(hypermedia.(reference." slug ")))")
|
:content (~sx-doc :path (str "/sx/(geography.(hypermedia.(reference." slug ")))")
|
||||||
(case slug
|
(case slug
|
||||||
"attributes" (~reference-attrs-content
|
"attributes" (~reference-attrs-content
|
||||||
:req-table (~doc-attr-table-from-data :title "Request Attributes" :attrs req-attrs)
|
:req-table (~doc-attr-table-from-data :title "Request Attributes" :attrs req-attrs)
|
||||||
@@ -98,7 +98,7 @@
|
|||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:data (attr-detail-data slug)
|
:data (attr-detail-data slug)
|
||||||
:content (~sx-doc :path (str "/(geography.(hypermedia.(reference-detail.attributes." slug ")))")
|
:content (~sx-doc :path (str "/sx/(geography.(hypermedia.(reference-detail.attributes." slug ")))")
|
||||||
(if attr-not-found
|
(if attr-not-found
|
||||||
(~reference-attr-not-found :slug slug)
|
(~reference-attr-not-found :slug slug)
|
||||||
(~reference-attr-detail-content
|
(~reference-attr-detail-content
|
||||||
@@ -114,7 +114,7 @@
|
|||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:data (header-detail-data slug)
|
:data (header-detail-data slug)
|
||||||
:content (~sx-doc :path (str "/(geography.(hypermedia.(reference-detail.headers." slug ")))")
|
:content (~sx-doc :path (str "/sx/(geography.(hypermedia.(reference-detail.headers." slug ")))")
|
||||||
(if header-not-found
|
(if header-not-found
|
||||||
(~reference-attr-not-found :slug slug)
|
(~reference-attr-not-found :slug slug)
|
||||||
(~reference-header-detail-content
|
(~reference-header-detail-content
|
||||||
@@ -129,7 +129,7 @@
|
|||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:data (event-detail-data slug)
|
:data (event-detail-data slug)
|
||||||
:content (~sx-doc :path (str "/(geography.(hypermedia.(reference-detail.events." slug ")))")
|
:content (~sx-doc :path (str "/sx/(geography.(hypermedia.(reference-detail.events." slug ")))")
|
||||||
(if event-not-found
|
(if event-not-found
|
||||||
(~reference-attr-not-found :slug slug)
|
(~reference-attr-not-found :slug slug)
|
||||||
(~reference-event-detail-content
|
(~reference-event-detail-content
|
||||||
@@ -146,7 +146,7 @@
|
|||||||
:path "/applications/"
|
:path "/applications/"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path "/(applications)"))
|
:content (~sx-doc :path "/sx/(applications)"))
|
||||||
|
|
||||||
;; ---------------------------------------------------------------------------
|
;; ---------------------------------------------------------------------------
|
||||||
;; Protocols section (under Applications)
|
;; Protocols section (under Applications)
|
||||||
@@ -156,13 +156,13 @@
|
|||||||
:path "/applications/protocols/"
|
:path "/applications/protocols/"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path "/(applications.(protocol))" (~protocol-wire-format-content)))
|
:content (~sx-doc :path "/sx/(applications.(protocol))" (~protocol-wire-format-content)))
|
||||||
|
|
||||||
(defpage protocol-page
|
(defpage protocol-page
|
||||||
:path "/applications/protocols/<slug>"
|
:path "/applications/protocols/<slug>"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path (str "/(applications.(protocol." slug "))")
|
:content (~sx-doc :path (str "/sx/(applications.(protocol." slug "))")
|
||||||
(case slug
|
(case slug
|
||||||
"wire-format" (~protocol-wire-format-content)
|
"wire-format" (~protocol-wire-format-content)
|
||||||
"fragments" (~protocol-fragments-content)
|
"fragments" (~protocol-fragments-content)
|
||||||
@@ -180,13 +180,13 @@
|
|||||||
:path "/geography/hypermedia/examples/"
|
:path "/geography/hypermedia/examples/"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path "/(geography.(hypermedia.(example)))"))
|
:content (~sx-doc :path "/sx/(geography.(hypermedia.(example)))"))
|
||||||
|
|
||||||
(defpage examples-page
|
(defpage examples-page
|
||||||
:path "/geography/hypermedia/examples/<slug>"
|
:path "/geography/hypermedia/examples/<slug>"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path (str "/(geography.(hypermedia.(example." slug ")))")
|
:content (~sx-doc :path (str "/sx/(geography.(hypermedia.(example." slug ")))")
|
||||||
(case slug
|
(case slug
|
||||||
"click-to-load" (~example-click-to-load)
|
"click-to-load" (~example-click-to-load)
|
||||||
"form-submission" (~example-form-submission)
|
"form-submission" (~example-form-submission)
|
||||||
@@ -225,7 +225,7 @@
|
|||||||
:path "/etc/"
|
:path "/etc/"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path "/(etc)"))
|
:content (~sx-doc :path "/sx/(etc)"))
|
||||||
|
|
||||||
;; ---------------------------------------------------------------------------
|
;; ---------------------------------------------------------------------------
|
||||||
;; Essays section (under Etc)
|
;; Essays section (under Etc)
|
||||||
@@ -235,13 +235,13 @@
|
|||||||
:path "/etc/essays/"
|
:path "/etc/essays/"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path "/(etc.(essay))" (~essays-index-content)))
|
:content (~sx-doc :path "/sx/(etc.(essay))" (~essays-index-content)))
|
||||||
|
|
||||||
(defpage essay-page
|
(defpage essay-page
|
||||||
:path "/etc/essays/<slug>"
|
:path "/etc/essays/<slug>"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path (str "/(etc.(essay." slug "))")
|
:content (~sx-doc :path (str "/sx/(etc.(essay." slug "))")
|
||||||
(case slug
|
(case slug
|
||||||
"sx-sucks" (~essay-sx-sucks)
|
"sx-sucks" (~essay-sx-sucks)
|
||||||
"why-sexps" (~essay-why-sexps)
|
"why-sexps" (~essay-why-sexps)
|
||||||
@@ -271,13 +271,13 @@
|
|||||||
:path "/etc/philosophy/"
|
:path "/etc/philosophy/"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path "/(etc.(philosophy))" (~philosophy-index-content)))
|
:content (~sx-doc :path "/sx/(etc.(philosophy))" (~philosophy-index-content)))
|
||||||
|
|
||||||
(defpage philosophy-page
|
(defpage philosophy-page
|
||||||
:path "/etc/philosophy/<slug>"
|
:path "/etc/philosophy/<slug>"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path (str "/(etc.(philosophy." slug "))")
|
:content (~sx-doc :path (str "/sx/(etc.(philosophy." slug "))")
|
||||||
(case slug
|
(case slug
|
||||||
"sx-manifesto" (~essay-sx-manifesto)
|
"sx-manifesto" (~essay-sx-manifesto)
|
||||||
"godel-escher-bach" (~essay-godel-escher-bach)
|
"godel-escher-bach" (~essay-godel-escher-bach)
|
||||||
@@ -294,13 +294,13 @@
|
|||||||
:path "/applications/cssx/"
|
:path "/applications/cssx/"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path "/(applications.(cssx))" (~cssx-overview-content)))
|
:content (~sx-doc :path "/sx/(applications.(cssx))" (~cssx-overview-content)))
|
||||||
|
|
||||||
(defpage cssx-page
|
(defpage cssx-page
|
||||||
:path "/applications/cssx/<slug>"
|
:path "/applications/cssx/<slug>"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path (str "/(applications.(cssx." slug "))")
|
:content (~sx-doc :path (str "/sx/(applications.(cssx." slug "))")
|
||||||
(case slug
|
(case slug
|
||||||
"patterns" (~cssx-patterns-content)
|
"patterns" (~cssx-patterns-content)
|
||||||
"delivery" (~cssx-delivery-content)
|
"delivery" (~cssx-delivery-content)
|
||||||
@@ -318,18 +318,18 @@
|
|||||||
:path "/language/specs/"
|
:path "/language/specs/"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path "/(language.(spec))" (~spec-architecture-content)))
|
:content (~sx-doc :path "/sx/(language.(spec))" (~spec-architecture-content)))
|
||||||
|
|
||||||
(defpage specs-page
|
(defpage specs-page
|
||||||
:path "/language/specs/<slug>"
|
:path "/language/specs/<slug>"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path (str "/(language.(spec." slug "))")
|
:content (~sx-doc :path (str "/sx/(language.(spec." slug "))")
|
||||||
(let ((make-spec-files (fn (items)
|
(let ((make-spec-files (fn (items)
|
||||||
(map (fn (item)
|
(map (fn (item)
|
||||||
(dict :title (get item "title") :desc (get item "desc")
|
(dict :title (get item "title") :desc (get item "desc")
|
||||||
:prose (get item "prose")
|
:prose (get item "prose")
|
||||||
:filename (get item "filename") :href (str "/(language.(spec." (get item "slug") "))")
|
:filename (get item "filename") :href (str "/sx/(language.(spec." (get item "slug") "))")
|
||||||
:source (read-spec-file (get item "filename"))))
|
:source (read-spec-file (get item "filename"))))
|
||||||
items))))
|
items))))
|
||||||
(case slug
|
(case slug
|
||||||
@@ -369,7 +369,7 @@
|
|||||||
:path "/language/specs/explore/<slug>"
|
:path "/language/specs/explore/<slug>"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path (str "/(language.(spec.(explore." slug ")))")
|
:content (~sx-doc :path (str "/sx/(language.(spec.(explore." slug ")))")
|
||||||
(let ((spec (find-spec slug)))
|
(let ((spec (find-spec slug)))
|
||||||
(if spec
|
(if spec
|
||||||
(let ((data (spec-explorer-data
|
(let ((data (spec-explorer-data
|
||||||
@@ -389,14 +389,14 @@
|
|||||||
:path "/language/bootstrappers/"
|
:path "/language/bootstrappers/"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path "/(language.(bootstrapper))" (~bootstrappers-index-content)))
|
:content (~sx-doc :path "/sx/(language.(bootstrapper))" (~bootstrappers-index-content)))
|
||||||
|
|
||||||
(defpage bootstrapper-page
|
(defpage bootstrapper-page
|
||||||
:path "/language/bootstrappers/<slug>"
|
:path "/language/bootstrappers/<slug>"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:data (bootstrapper-data slug)
|
:data (bootstrapper-data slug)
|
||||||
:content (~sx-doc :path (str "/(language.(bootstrapper." slug "))")
|
:content (~sx-doc :path (str "/sx/(language.(bootstrapper." slug "))")
|
||||||
(if bootstrapper-not-found
|
(if bootstrapper-not-found
|
||||||
(~spec-not-found :slug slug)
|
(~spec-not-found :slug slug)
|
||||||
(case slug
|
(case slug
|
||||||
@@ -434,14 +434,14 @@
|
|||||||
:path "/geography/isomorphism/"
|
:path "/geography/isomorphism/"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path "/(geography.(isomorphism))" (~plan-isomorphic-content)))
|
:content (~sx-doc :path "/sx/(geography.(isomorphism))" (~plan-isomorphic-content)))
|
||||||
|
|
||||||
(defpage bundle-analyzer
|
(defpage bundle-analyzer
|
||||||
:path "/geography/isomorphism/bundle-analyzer"
|
:path "/geography/isomorphism/bundle-analyzer"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:data (bundle-analyzer-data)
|
:data (bundle-analyzer-data)
|
||||||
:content (~sx-doc :path "/(geography.(isomorphism.bundle-analyzer))"
|
:content (~sx-doc :path "/sx/(geography.(isomorphism.bundle-analyzer))"
|
||||||
(~bundle-analyzer-content
|
(~bundle-analyzer-content
|
||||||
:pages pages :total-components total-components :total-macros total-macros
|
:pages pages :total-components total-components :total-macros total-macros
|
||||||
:pure-count pure-count :io-count io-count)))
|
:pure-count pure-count :io-count io-count)))
|
||||||
@@ -451,7 +451,7 @@
|
|||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:data (routing-analyzer-data)
|
:data (routing-analyzer-data)
|
||||||
:content (~sx-doc :path "/(geography.(isomorphism.routing-analyzer))"
|
:content (~sx-doc :path "/sx/(geography.(isomorphism.routing-analyzer))"
|
||||||
(~routing-analyzer-content
|
(~routing-analyzer-content
|
||||||
:pages pages :total-pages total-pages :client-count client-count
|
:pages pages :total-pages total-pages :client-count client-count
|
||||||
:server-count server-count :registry-sample registry-sample)))
|
:server-count server-count :registry-sample registry-sample)))
|
||||||
@@ -461,7 +461,7 @@
|
|||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:data (data-test-data)
|
:data (data-test-data)
|
||||||
:content (~sx-doc :path "/(geography.(isomorphism.data-test))"
|
:content (~sx-doc :path "/sx/(geography.(isomorphism.data-test))"
|
||||||
(~data-test-content
|
(~data-test-content
|
||||||
:server-time server-time :items items
|
:server-time server-time :items items
|
||||||
:phase phase :transport transport)))
|
:phase phase :transport transport)))
|
||||||
@@ -470,14 +470,14 @@
|
|||||||
:path "/geography/isomorphism/async-io"
|
:path "/geography/isomorphism/async-io"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path "/(geography.(isomorphism.async-io))" (~async-io-demo-content)))
|
:content (~sx-doc :path "/sx/(geography.(isomorphism.async-io))" (~async-io-demo-content)))
|
||||||
|
|
||||||
(defpage streaming-demo
|
(defpage streaming-demo
|
||||||
:path "/geography/isomorphism/streaming"
|
:path "/geography/isomorphism/streaming"
|
||||||
:auth :public
|
:auth :public
|
||||||
:stream true
|
:stream true
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:shell (~sx-doc :path "/(geography.(isomorphism.streaming))"
|
:shell (~sx-doc :path "/sx/(geography.(isomorphism.streaming))"
|
||||||
(~streaming-demo-layout
|
(~streaming-demo-layout
|
||||||
(~suspense :id "stream-fast" :fallback (~stream-skeleton))
|
(~suspense :id "stream-fast" :fallback (~stream-skeleton))
|
||||||
(~suspense :id "stream-medium" :fallback (~stream-skeleton))
|
(~suspense :id "stream-medium" :fallback (~stream-skeleton))
|
||||||
@@ -494,7 +494,7 @@
|
|||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:data (affinity-demo-data)
|
:data (affinity-demo-data)
|
||||||
:content (~sx-doc :path "/(geography.(isomorphism.affinity))"
|
:content (~sx-doc :path "/sx/(geography.(isomorphism.affinity))"
|
||||||
(~affinity-demo-content :components components :page-plans page-plans)))
|
(~affinity-demo-content :components components :page-plans page-plans)))
|
||||||
|
|
||||||
(defpage optimistic-demo
|
(defpage optimistic-demo
|
||||||
@@ -502,7 +502,7 @@
|
|||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:data (optimistic-demo-data)
|
:data (optimistic-demo-data)
|
||||||
:content (~sx-doc :path "/(geography.(isomorphism.optimistic))"
|
:content (~sx-doc :path "/sx/(geography.(isomorphism.optimistic))"
|
||||||
(~optimistic-demo-content :items items :server-time server-time)))
|
(~optimistic-demo-content :items items :server-time server-time)))
|
||||||
|
|
||||||
(defpage offline-demo
|
(defpage offline-demo
|
||||||
@@ -510,7 +510,7 @@
|
|||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:data (offline-demo-data)
|
:data (offline-demo-data)
|
||||||
:content (~sx-doc :path "/(geography.(isomorphism.offline))"
|
:content (~sx-doc :path "/sx/(geography.(isomorphism.offline))"
|
||||||
(~offline-demo-content :notes notes :server-time server-time)))
|
(~offline-demo-content :notes notes :server-time server-time)))
|
||||||
|
|
||||||
;; Wildcard must come AFTER specific routes (first-match routing)
|
;; Wildcard must come AFTER specific routes (first-match routing)
|
||||||
@@ -518,7 +518,7 @@
|
|||||||
:path "/geography/isomorphism/<slug>"
|
:path "/geography/isomorphism/<slug>"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path (str "/(geography.(isomorphism." slug "))")
|
:content (~sx-doc :path (str "/sx/(geography.(isomorphism." slug "))")
|
||||||
(case slug
|
(case slug
|
||||||
"bundle-analyzer" (~bundle-analyzer-content
|
"bundle-analyzer" (~bundle-analyzer-content
|
||||||
:pages pages :total-components total-components :total-macros total-macros
|
:pages pages :total-components total-components :total-macros total-macros
|
||||||
@@ -536,7 +536,7 @@
|
|||||||
:path "/etc/plans/"
|
:path "/etc/plans/"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path "/(etc.(plan))" (~plans-index-content)))
|
:content (~sx-doc :path "/sx/(etc.(plan))" (~plans-index-content)))
|
||||||
|
|
||||||
(defpage plan-page
|
(defpage plan-page
|
||||||
:path "/etc/plans/<slug>"
|
:path "/etc/plans/<slug>"
|
||||||
@@ -545,7 +545,7 @@
|
|||||||
:data (case slug
|
:data (case slug
|
||||||
"theorem-prover" (prove-data)
|
"theorem-prover" (prove-data)
|
||||||
:else nil)
|
:else nil)
|
||||||
:content (~sx-doc :path (str "/(etc.(plan." slug "))")
|
:content (~sx-doc :path (str "/sx/(etc.(plan." slug "))")
|
||||||
(case slug
|
(case slug
|
||||||
"status" (~plan-status-content)
|
"status" (~plan-status-content)
|
||||||
"reader-macros" (~plan-reader-macros-content)
|
"reader-macros" (~plan-reader-macros-content)
|
||||||
@@ -586,7 +586,7 @@
|
|||||||
:path "/geography/"
|
:path "/geography/"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path "/(geography)"))
|
:content (~sx-doc :path "/sx/(geography)"))
|
||||||
|
|
||||||
;; ---------------------------------------------------------------------------
|
;; ---------------------------------------------------------------------------
|
||||||
;; Reactive Islands section (under Geography)
|
;; Reactive Islands section (under Geography)
|
||||||
@@ -596,13 +596,13 @@
|
|||||||
:path "/geography/reactive/"
|
:path "/geography/reactive/"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path "/(geography.(reactive))" (~reactive-islands-index-content)))
|
:content (~sx-doc :path "/sx/(geography.(reactive))" (~reactive-islands-index-content)))
|
||||||
|
|
||||||
(defpage reactive-islands-page
|
(defpage reactive-islands-page
|
||||||
:path "/geography/reactive/<slug>"
|
:path "/geography/reactive/<slug>"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path (str "/(geography.(reactive." slug "))")
|
:content (~sx-doc :path (str "/sx/(geography.(reactive." slug "))")
|
||||||
(case slug
|
(case slug
|
||||||
"demo" (~reactive-islands-demo-content)
|
"demo" (~reactive-islands-demo-content)
|
||||||
"event-bridge" (~reactive-islands-event-bridge-content)
|
"event-bridge" (~reactive-islands-event-bridge-content)
|
||||||
@@ -619,7 +619,7 @@
|
|||||||
:path "/geography/marshes/"
|
:path "/geography/marshes/"
|
||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:content (~sx-doc :path "/(geography.(marshes))" (~reactive-islands-marshes-content)))
|
:content (~sx-doc :path "/sx/(geography.(marshes))" (~reactive-islands-marshes-content)))
|
||||||
|
|
||||||
;; ---------------------------------------------------------------------------
|
;; ---------------------------------------------------------------------------
|
||||||
;; Bootstrapped page helpers demo
|
;; Bootstrapped page helpers demo
|
||||||
@@ -630,7 +630,7 @@
|
|||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:data (page-helpers-demo-data)
|
:data (page-helpers-demo-data)
|
||||||
:content (~sx-doc :path "/(language.(bootstrapper.page-helpers))"
|
:content (~sx-doc :path "/sx/(language.(bootstrapper.page-helpers))"
|
||||||
(~page-helpers-demo-content
|
(~page-helpers-demo-content
|
||||||
:sf-categories sf-categories :sf-total sf-total :sf-ms sf-ms
|
:sf-categories sf-categories :sf-total sf-total :sf-ms sf-ms
|
||||||
:ref-sample ref-sample :ref-ms ref-ms
|
:ref-sample ref-sample :ref-ms ref-ms
|
||||||
@@ -652,7 +652,7 @@
|
|||||||
:auth :public
|
:auth :public
|
||||||
:layout :sx-docs
|
:layout :sx-docs
|
||||||
:data (run-modular-tests "all")
|
:data (run-modular-tests "all")
|
||||||
:content (~sx-doc :path "/(language.(test))"
|
:content (~sx-doc :path "/sx/(language.(test))"
|
||||||
(~testing-overview-content
|
(~testing-overview-content
|
||||||
:server-results server-results
|
:server-results server-results
|
||||||
:framework-source framework-source
|
:framework-source framework-source
|
||||||
@@ -676,7 +676,7 @@
|
|||||||
"engine" (run-modular-tests "engine")
|
"engine" (run-modular-tests "engine")
|
||||||
"orchestration" (run-modular-tests "orchestration")
|
"orchestration" (run-modular-tests "orchestration")
|
||||||
:else (dict))
|
:else (dict))
|
||||||
:content (~sx-doc :path (str "/(language.(test." slug "))")
|
:content (~sx-doc :path (str "/sx/(language.(test." slug "))")
|
||||||
(case slug
|
(case slug
|
||||||
"eval" (~testing-spec-content
|
"eval" (~testing-spec-content
|
||||||
:spec-name "eval"
|
:spec-name "eval"
|
||||||
|
|||||||
@@ -214,8 +214,8 @@ async def eval_sx_url(raw_path: str) -> Any:
|
|||||||
|
|
||||||
ctx = _get_request_context()
|
ctx = _get_request_context()
|
||||||
|
|
||||||
# Use raw URL path for nav matching — dots in hrefs match dots in URLs
|
# Nav hrefs use /sx/ prefix — reconstruct the full path for nav matching
|
||||||
path_str = raw_path
|
path_str = f"/sx{raw_path}" if raw_path != "/" else "/sx/"
|
||||||
|
|
||||||
# Check if expression head is a component (~name) — if so, skip
|
# Check if expression head is a component (~name) — if so, skip
|
||||||
# async_eval and pass directly to _eval_slot. Components contain HTML
|
# async_eval and pass directly to _eval_slot. Components contain HTML
|
||||||
@@ -285,109 +285,109 @@ _REDIRECT_PATTERNS = [
|
|||||||
|
|
||||||
# Reference API: /geography/hypermedia/reference/api/item/<id>
|
# Reference API: /geography/hypermedia/reference/api/item/<id>
|
||||||
(re.compile(r"^/geography/hypermedia/reference/api/item/(.+?)/?$"),
|
(re.compile(r"^/geography/hypermedia/reference/api/item/(.+?)/?$"),
|
||||||
lambda m: f"/(geography.(hypermedia.(reference.(api.(item.{m.group(1)})))))"),
|
lambda m: f"/sx/(geography.(hypermedia.(reference.(api.(item.{m.group(1)})))))"),
|
||||||
# Reference API: /geography/hypermedia/reference/api/<name>
|
# Reference API: /geography/hypermedia/reference/api/<name>
|
||||||
(re.compile(r"^/geography/hypermedia/reference/api/(.+?)/?$"),
|
(re.compile(r"^/geography/hypermedia/reference/api/(.+?)/?$"),
|
||||||
lambda m: f"/(geography.(hypermedia.(reference.(api.{m.group(1)}))))"),
|
lambda m: f"/sx/(geography.(hypermedia.(reference.(api.{m.group(1)}))))"),
|
||||||
|
|
||||||
# Example API: sub-paths with params
|
# Example API: sub-paths with params
|
||||||
(re.compile(r"^/geography/hypermedia/examples/api/editrow/(.+?)/cancel/?$"),
|
(re.compile(r"^/geography/hypermedia/examples/api/editrow/(.+?)/cancel/?$"),
|
||||||
lambda m: f"/(geography.(hypermedia.(example.(api.(editrow-cancel.{m.group(1)})))))"),
|
lambda m: f"/sx/(geography.(hypermedia.(example.(api.(editrow-cancel.{m.group(1)})))))"),
|
||||||
(re.compile(r"^/geography/hypermedia/examples/api/editrow/(.+?)/?$"),
|
(re.compile(r"^/geography/hypermedia/examples/api/editrow/(.+?)/?$"),
|
||||||
lambda m: f"/(geography.(hypermedia.(example.(api.(editrow.{m.group(1)})))))"),
|
lambda m: f"/sx/(geography.(hypermedia.(example.(api.(editrow.{m.group(1)})))))"),
|
||||||
(re.compile(r"^/geography/hypermedia/examples/api/delete/(.+?)/?$"),
|
(re.compile(r"^/geography/hypermedia/examples/api/delete/(.+?)/?$"),
|
||||||
lambda m: f"/(geography.(hypermedia.(example.(api.(delete.{m.group(1)})))))"),
|
lambda m: f"/sx/(geography.(hypermedia.(example.(api.(delete.{m.group(1)})))))"),
|
||||||
(re.compile(r"^/geography/hypermedia/examples/api/tabs/(.+?)/?$"),
|
(re.compile(r"^/geography/hypermedia/examples/api/tabs/(.+?)/?$"),
|
||||||
lambda m: f"/(geography.(hypermedia.(example.(api.(tabs.{m.group(1)})))))"),
|
lambda m: f"/sx/(geography.(hypermedia.(example.(api.(tabs.{m.group(1)})))))"),
|
||||||
# Example API: sub-paths collapsed to hyphens
|
# Example API: sub-paths collapsed to hyphens
|
||||||
(re.compile(r"^/geography/hypermedia/examples/api/edit/cancel/?$"),
|
(re.compile(r"^/geography/hypermedia/examples/api/edit/cancel/?$"),
|
||||||
"/(geography.(hypermedia.(example.(api.edit-cancel))))"),
|
"/sx/(geography.(hypermedia.(example.(api.edit-cancel))))"),
|
||||||
(re.compile(r"^/geography/hypermedia/examples/api/progress/start/?$"),
|
(re.compile(r"^/geography/hypermedia/examples/api/progress/start/?$"),
|
||||||
"/(geography.(hypermedia.(example.(api.progress-start))))"),
|
"/sx/(geography.(hypermedia.(example.(api.progress-start))))"),
|
||||||
(re.compile(r"^/geography/hypermedia/examples/api/progress/status/?$"),
|
(re.compile(r"^/geography/hypermedia/examples/api/progress/status/?$"),
|
||||||
"/(geography.(hypermedia.(example.(api.progress-status))))"),
|
"/sx/(geography.(hypermedia.(example.(api.progress-status))))"),
|
||||||
(re.compile(r"^/geography/hypermedia/examples/api/validate/submit/?$"),
|
(re.compile(r"^/geography/hypermedia/examples/api/validate/submit/?$"),
|
||||||
"/(geography.(hypermedia.(example.(api.validate-submit))))"),
|
"/sx/(geography.(hypermedia.(example.(api.validate-submit))))"),
|
||||||
(re.compile(r"^/geography/hypermedia/examples/api/dialog/close/?$"),
|
(re.compile(r"^/geography/hypermedia/examples/api/dialog/close/?$"),
|
||||||
"/(geography.(hypermedia.(example.(api.dialog-close))))"),
|
"/sx/(geography.(hypermedia.(example.(api.dialog-close))))"),
|
||||||
(re.compile(r"^/geography/hypermedia/examples/api/putpatch/edit-all/?$"),
|
(re.compile(r"^/geography/hypermedia/examples/api/putpatch/edit-all/?$"),
|
||||||
"/(geography.(hypermedia.(example.(api.putpatch-edit-all))))"),
|
"/sx/(geography.(hypermedia.(example.(api.putpatch-edit-all))))"),
|
||||||
(re.compile(r"^/geography/hypermedia/examples/api/putpatch/cancel/?$"),
|
(re.compile(r"^/geography/hypermedia/examples/api/putpatch/cancel/?$"),
|
||||||
"/(geography.(hypermedia.(example.(api.putpatch-cancel))))"),
|
"/sx/(geography.(hypermedia.(example.(api.putpatch-cancel))))"),
|
||||||
# Example API: simple names
|
# Example API: simple names
|
||||||
(re.compile(r"^/geography/hypermedia/examples/api/(.+?)/?$"),
|
(re.compile(r"^/geography/hypermedia/examples/api/(.+?)/?$"),
|
||||||
lambda m: f"/(geography.(hypermedia.(example.(api.{m.group(1)}))))"),
|
lambda m: f"/sx/(geography.(hypermedia.(example.(api.{m.group(1)}))))"),
|
||||||
|
|
||||||
# Reactive API: sub-paths collapsed
|
# Reactive API: sub-paths collapsed
|
||||||
(re.compile(r"^/geography/reactive/api/search/(.+?)/?$"),
|
(re.compile(r"^/geography/reactive/api/search/(.+?)/?$"),
|
||||||
lambda m: f"/(geography.(reactive.(api.search-{m.group(1)})))"),
|
lambda m: f"/sx/(geography.(reactive.(api.search-{m.group(1)})))"),
|
||||||
(re.compile(r"^/geography/reactive/api/(.+?)/?$"),
|
(re.compile(r"^/geography/reactive/api/(.+?)/?$"),
|
||||||
lambda m: f"/(geography.(reactive.(api.{m.group(1)})))"),
|
lambda m: f"/sx/(geography.(reactive.(api.{m.group(1)})))"),
|
||||||
|
|
||||||
# --- Page redirects ---
|
# --- Page redirects ---
|
||||||
|
|
||||||
# More specific first
|
# More specific first
|
||||||
(re.compile(r"^/language/specs/explore/(.+?)/?$"),
|
(re.compile(r"^/language/specs/explore/(.+?)/?$"),
|
||||||
lambda m: f"/(language.(spec.(explore.{m.group(1)})))"),
|
lambda m: f"/sx/(language.(spec.(explore.{m.group(1)})))"),
|
||||||
(re.compile(r"^/language/docs/(.+?)/?$"),
|
(re.compile(r"^/language/docs/(.+?)/?$"),
|
||||||
lambda m: f"/(language.(doc.{m.group(1)}))"),
|
lambda m: f"/sx/(language.(doc.{m.group(1)}))"),
|
||||||
(re.compile(r"^/language/docs/?$"),
|
(re.compile(r"^/language/docs/?$"),
|
||||||
"/(language.(doc))"),
|
"/sx/(language.(doc))"),
|
||||||
(re.compile(r"^/language/specs/(.+?)/?$"),
|
(re.compile(r"^/language/specs/(.+?)/?$"),
|
||||||
lambda m: f"/(language.(spec.{m.group(1)}))"),
|
lambda m: f"/sx/(language.(spec.{m.group(1)}))"),
|
||||||
(re.compile(r"^/language/bootstrappers/page-helpers/?$"),
|
(re.compile(r"^/language/bootstrappers/page-helpers/?$"),
|
||||||
"/(language.(bootstrapper.page-helpers))"),
|
"/sx/(language.(bootstrapper.page-helpers))"),
|
||||||
(re.compile(r"^/language/bootstrappers/(.+?)/?$"),
|
(re.compile(r"^/language/bootstrappers/(.+?)/?$"),
|
||||||
lambda m: f"/(language.(bootstrapper.{m.group(1)}))"),
|
lambda m: f"/sx/(language.(bootstrapper.{m.group(1)}))"),
|
||||||
(re.compile(r"^/language/testing/(.+?)/?$"),
|
(re.compile(r"^/language/testing/(.+?)/?$"),
|
||||||
lambda m: f"/(language.(test.{m.group(1)}))"),
|
lambda m: f"/sx/(language.(test.{m.group(1)}))"),
|
||||||
(re.compile(r"^/geography/hypermedia/reference/attributes/(.+?)/?$"),
|
(re.compile(r"^/geography/hypermedia/reference/attributes/(.+?)/?$"),
|
||||||
lambda m: f"/(geography.(hypermedia.(reference-detail.attributes.{m.group(1)})))"),
|
lambda m: f"/sx/(geography.(hypermedia.(reference-detail.attributes.{m.group(1)})))"),
|
||||||
(re.compile(r"^/geography/hypermedia/reference/headers/(.+?)/?$"),
|
(re.compile(r"^/geography/hypermedia/reference/headers/(.+?)/?$"),
|
||||||
lambda m: f"/(geography.(hypermedia.(reference-detail.headers.{m.group(1)})))"),
|
lambda m: f"/sx/(geography.(hypermedia.(reference-detail.headers.{m.group(1)})))"),
|
||||||
(re.compile(r"^/geography/hypermedia/reference/events/(.+?)/?$"),
|
(re.compile(r"^/geography/hypermedia/reference/events/(.+?)/?$"),
|
||||||
lambda m: f"/(geography.(hypermedia.(reference-detail.events.{m.group(1)})))"),
|
lambda m: f"/sx/(geography.(hypermedia.(reference-detail.events.{m.group(1)})))"),
|
||||||
(re.compile(r"^/geography/hypermedia/reference/(.+?)/?$"),
|
(re.compile(r"^/geography/hypermedia/reference/(.+?)/?$"),
|
||||||
lambda m: f"/(geography.(hypermedia.(reference.{m.group(1)})))"),
|
lambda m: f"/sx/(geography.(hypermedia.(reference.{m.group(1)})))"),
|
||||||
(re.compile(r"^/geography/hypermedia/examples/(.+?)/?$"),
|
(re.compile(r"^/geography/hypermedia/examples/(.+?)/?$"),
|
||||||
lambda m: f"/(geography.(hypermedia.(example.{m.group(1)})))"),
|
lambda m: f"/sx/(geography.(hypermedia.(example.{m.group(1)})))"),
|
||||||
(re.compile(r"^/geography/hypermedia/reference/?$"),
|
(re.compile(r"^/geography/hypermedia/reference/?$"),
|
||||||
"/(geography.(hypermedia.(reference)))"),
|
"/sx/(geography.(hypermedia.(reference)))"),
|
||||||
(re.compile(r"^/geography/hypermedia/examples/?$"),
|
(re.compile(r"^/geography/hypermedia/examples/?$"),
|
||||||
"/(geography.(hypermedia.(example)))"),
|
"/sx/(geography.(hypermedia.(example)))"),
|
||||||
(re.compile(r"^/geography/reactive/(.+?)/?$"),
|
(re.compile(r"^/geography/reactive/(.+?)/?$"),
|
||||||
lambda m: f"/(geography.(reactive.{m.group(1)}))"),
|
lambda m: f"/sx/(geography.(reactive.{m.group(1)}))"),
|
||||||
(re.compile(r"^/geography/isomorphism/(.+?)/?$"),
|
(re.compile(r"^/geography/isomorphism/(.+?)/?$"),
|
||||||
lambda m: f"/(geography.(isomorphism.{m.group(1)}))"),
|
lambda m: f"/sx/(geography.(isomorphism.{m.group(1)}))"),
|
||||||
(re.compile(r"^/geography/marshes/?$"),
|
(re.compile(r"^/geography/marshes/?$"),
|
||||||
"/(geography.(marshes))"),
|
"/sx/(geography.(marshes))"),
|
||||||
(re.compile(r"^/applications/cssx/(.+?)/?$"),
|
(re.compile(r"^/applications/cssx/(.+?)/?$"),
|
||||||
lambda m: f"/(applications.(cssx.{m.group(1)}))"),
|
lambda m: f"/sx/(applications.(cssx.{m.group(1)}))"),
|
||||||
(re.compile(r"^/applications/protocols/(.+?)/?$"),
|
(re.compile(r"^/applications/protocols/(.+?)/?$"),
|
||||||
lambda m: f"/(applications.(protocol.{m.group(1)}))"),
|
lambda m: f"/sx/(applications.(protocol.{m.group(1)}))"),
|
||||||
(re.compile(r"^/etc/essays/(.+?)/?$"),
|
(re.compile(r"^/etc/essays/(.+?)/?$"),
|
||||||
lambda m: f"/(etc.(essay.{m.group(1)}))"),
|
lambda m: f"/sx/(etc.(essay.{m.group(1)}))"),
|
||||||
(re.compile(r"^/etc/philosophy/(.+?)/?$"),
|
(re.compile(r"^/etc/philosophy/(.+?)/?$"),
|
||||||
lambda m: f"/(etc.(philosophy.{m.group(1)}))"),
|
lambda m: f"/sx/(etc.(philosophy.{m.group(1)}))"),
|
||||||
(re.compile(r"^/etc/plans/(.+?)/?$"),
|
(re.compile(r"^/etc/plans/(.+?)/?$"),
|
||||||
lambda m: f"/(etc.(plan.{m.group(1)}))"),
|
lambda m: f"/sx/(etc.(plan.{m.group(1)}))"),
|
||||||
# Section indices
|
# Section indices
|
||||||
(re.compile(r"^/language/docs/?$"), "/(language.(doc))"),
|
(re.compile(r"^/language/docs/?$"), "/sx/(language.(doc))"),
|
||||||
(re.compile(r"^/language/specs/?$"), "/(language.(spec))"),
|
(re.compile(r"^/language/specs/?$"), "/sx/(language.(spec))"),
|
||||||
(re.compile(r"^/language/bootstrappers/?$"), "/(language.(bootstrapper))"),
|
(re.compile(r"^/language/bootstrappers/?$"), "/sx/(language.(bootstrapper))"),
|
||||||
(re.compile(r"^/language/testing/?$"), "/(language.(test))"),
|
(re.compile(r"^/language/testing/?$"), "/sx/(language.(test))"),
|
||||||
(re.compile(r"^/language/?$"), "/(language)"),
|
(re.compile(r"^/language/?$"), "/sx/(language)"),
|
||||||
(re.compile(r"^/geography/hypermedia/?$"), "/(geography.(hypermedia))"),
|
(re.compile(r"^/geography/hypermedia/?$"), "/sx/(geography.(hypermedia))"),
|
||||||
(re.compile(r"^/geography/reactive/?$"), "/(geography.(reactive))"),
|
(re.compile(r"^/geography/reactive/?$"), "/sx/(geography.(reactive))"),
|
||||||
(re.compile(r"^/geography/isomorphism/?$"), "/(geography.(isomorphism))"),
|
(re.compile(r"^/geography/isomorphism/?$"), "/sx/(geography.(isomorphism))"),
|
||||||
(re.compile(r"^/geography/?$"), "/(geography)"),
|
(re.compile(r"^/geography/?$"), "/sx/(geography)"),
|
||||||
(re.compile(r"^/applications/cssx/?$"), "/(applications.(cssx))"),
|
(re.compile(r"^/applications/cssx/?$"), "/sx/(applications.(cssx))"),
|
||||||
(re.compile(r"^/applications/protocols/?$"), "/(applications.(protocol))"),
|
(re.compile(r"^/applications/protocols/?$"), "/sx/(applications.(protocol))"),
|
||||||
(re.compile(r"^/applications/?$"), "/(applications)"),
|
(re.compile(r"^/applications/?$"), "/sx/(applications)"),
|
||||||
(re.compile(r"^/etc/essays/?$"), "/(etc.(essay))"),
|
(re.compile(r"^/etc/essays/?$"), "/sx/(etc.(essay))"),
|
||||||
(re.compile(r"^/etc/philosophy/?$"), "/(etc.(philosophy))"),
|
(re.compile(r"^/etc/philosophy/?$"), "/sx/(etc.(philosophy))"),
|
||||||
(re.compile(r"^/etc/plans/?$"), "/(etc.(plan))"),
|
(re.compile(r"^/etc/plans/?$"), "/sx/(etc.(plan))"),
|
||||||
(re.compile(r"^/etc/?$"), "/(etc)"),
|
(re.compile(r"^/etc/?$"), "/sx/(etc)"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
(defcomp ~ref-get-demo ()
|
(defcomp ~ref-get-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(reference.(api.time))))"
|
:sx-get "/sx/(geography.(hypermedia.(reference.(api.time))))"
|
||||||
:sx-target "#ref-get-result"
|
:sx-target "#ref-get-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
(defcomp ~ref-post-demo ()
|
(defcomp ~ref-post-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(form
|
(form
|
||||||
:sx-post "/(geography.(hypermedia.(reference.(api.greet))))"
|
:sx-post "/sx/(geography.(hypermedia.(reference.(api.greet))))"
|
||||||
:sx-target "#ref-post-result"
|
:sx-target "#ref-post-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "flex gap-2"
|
:class "flex gap-2"
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
(div :class "flex items-center justify-between p-3 bg-stone-100 rounded"
|
(div :class "flex items-center justify-between p-3 bg-stone-100 rounded"
|
||||||
(span :class "text-stone-700 text-sm" "Status: " (strong "draft"))
|
(span :class "text-stone-700 text-sm" "Status: " (strong "draft"))
|
||||||
(button
|
(button
|
||||||
:sx-put "/(geography.(hypermedia.(reference.(api.status))))"
|
:sx-put "/sx/(geography.(hypermedia.(reference.(api.status))))"
|
||||||
:sx-target "#ref-put-view"
|
:sx-target "#ref-put-view"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-vals "{\"status\": \"published\"}"
|
:sx-vals "{\"status\": \"published\"}"
|
||||||
@@ -63,17 +63,17 @@
|
|||||||
(div :class "space-y-2"
|
(div :class "space-y-2"
|
||||||
(div :id "ref-del-1" :class "flex items-center justify-between p-2 border border-stone-200 rounded"
|
(div :id "ref-del-1" :class "flex items-center justify-between p-2 border border-stone-200 rounded"
|
||||||
(span :class "text-sm text-stone-700" "Item A")
|
(span :class "text-sm text-stone-700" "Item A")
|
||||||
(button :sx-delete "/(geography.(hypermedia.(reference.(api.(item.1)))))"
|
(button :sx-delete "/sx/(geography.(hypermedia.(reference.(api.(item.1)))))"
|
||||||
:sx-target "#ref-del-1" :sx-swap "delete"
|
:sx-target "#ref-del-1" :sx-swap "delete"
|
||||||
:class "text-red-500 text-sm hover:text-red-700" "Remove"))
|
:class "text-red-500 text-sm hover:text-red-700" "Remove"))
|
||||||
(div :id "ref-del-2" :class "flex items-center justify-between p-2 border border-stone-200 rounded"
|
(div :id "ref-del-2" :class "flex items-center justify-between p-2 border border-stone-200 rounded"
|
||||||
(span :class "text-sm text-stone-700" "Item B")
|
(span :class "text-sm text-stone-700" "Item B")
|
||||||
(button :sx-delete "/(geography.(hypermedia.(reference.(api.(item.2)))))"
|
(button :sx-delete "/sx/(geography.(hypermedia.(reference.(api.(item.2)))))"
|
||||||
:sx-target "#ref-del-2" :sx-swap "delete"
|
:sx-target "#ref-del-2" :sx-swap "delete"
|
||||||
:class "text-red-500 text-sm hover:text-red-700" "Remove"))
|
:class "text-red-500 text-sm hover:text-red-700" "Remove"))
|
||||||
(div :id "ref-del-3" :class "flex items-center justify-between p-2 border border-stone-200 rounded"
|
(div :id "ref-del-3" :class "flex items-center justify-between p-2 border border-stone-200 rounded"
|
||||||
(span :class "text-sm text-stone-700" "Item C")
|
(span :class "text-sm text-stone-700" "Item C")
|
||||||
(button :sx-delete "/(geography.(hypermedia.(reference.(api.(item.3)))))"
|
(button :sx-delete "/sx/(geography.(hypermedia.(reference.(api.(item.3)))))"
|
||||||
:sx-target "#ref-del-3" :sx-swap "delete"
|
:sx-target "#ref-del-3" :sx-swap "delete"
|
||||||
:class "text-red-500 text-sm hover:text-red-700" "Remove"))))
|
:class "text-red-500 text-sm hover:text-red-700" "Remove"))))
|
||||||
|
|
||||||
@@ -86,11 +86,11 @@
|
|||||||
(div :class "p-3 bg-stone-100 rounded"
|
(div :class "p-3 bg-stone-100 rounded"
|
||||||
(span :class "text-stone-700 text-sm" "Theme: " (strong :id "ref-patch-val" "light")))
|
(span :class "text-stone-700 text-sm" "Theme: " (strong :id "ref-patch-val" "light")))
|
||||||
(div :class "flex gap-2"
|
(div :class "flex gap-2"
|
||||||
(button :sx-patch "/(geography.(hypermedia.(reference.(api.theme))))"
|
(button :sx-patch "/sx/(geography.(hypermedia.(reference.(api.theme))))"
|
||||||
:sx-vals "{\"theme\": \"dark\"}"
|
:sx-vals "{\"theme\": \"dark\"}"
|
||||||
:sx-target "#ref-patch-val" :sx-swap "innerHTML"
|
:sx-target "#ref-patch-val" :sx-swap "innerHTML"
|
||||||
:class "px-3 py-1 bg-stone-800 text-white rounded text-sm" "Dark")
|
:class "px-3 py-1 bg-stone-800 text-white rounded text-sm" "Dark")
|
||||||
(button :sx-patch "/(geography.(hypermedia.(reference.(api.theme))))"
|
(button :sx-patch "/sx/(geography.(hypermedia.(reference.(api.theme))))"
|
||||||
:sx-vals "{\"theme\": \"light\"}"
|
:sx-vals "{\"theme\": \"light\"}"
|
||||||
:sx-target "#ref-patch-val" :sx-swap "innerHTML"
|
:sx-target "#ref-patch-val" :sx-swap "innerHTML"
|
||||||
:class "px-3 py-1 bg-stone-100 border border-stone-300 text-stone-700 rounded text-sm" "Light"))))
|
:class "px-3 py-1 bg-stone-100 border border-stone-300 text-stone-700 rounded text-sm" "Light"))))
|
||||||
@@ -102,7 +102,7 @@
|
|||||||
(defcomp ~ref-trigger-demo ()
|
(defcomp ~ref-trigger-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(input :type "text" :name "q" :placeholder "Type to search..."
|
(input :type "text" :name "q" :placeholder "Type to search..."
|
||||||
:sx-get "/(geography.(hypermedia.(reference.(api.trigger-search))))"
|
:sx-get "/sx/(geography.(hypermedia.(reference.(api.trigger-search))))"
|
||||||
:sx-trigger "input changed delay:300ms"
|
:sx-trigger "input changed delay:300ms"
|
||||||
:sx-target "#ref-trigger-result"
|
:sx-target "#ref-trigger-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
@@ -118,12 +118,12 @@
|
|||||||
(defcomp ~ref-target-demo ()
|
(defcomp ~ref-target-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(div :class "flex gap-2"
|
(div :class "flex gap-2"
|
||||||
(button :sx-get "/(geography.(hypermedia.(reference.(api.time))))"
|
(button :sx-get "/sx/(geography.(hypermedia.(reference.(api.time))))"
|
||||||
:sx-target "#ref-target-a"
|
:sx-target "#ref-target-a"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "px-3 py-1 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
:class "px-3 py-1 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
||||||
"Update Box A")
|
"Update Box A")
|
||||||
(button :sx-get "/(geography.(hypermedia.(reference.(api.time))))"
|
(button :sx-get "/sx/(geography.(hypermedia.(reference.(api.time))))"
|
||||||
:sx-target "#ref-target-b"
|
:sx-target "#ref-target-b"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "px-3 py-1 bg-emerald-600 text-white rounded text-sm hover:bg-emerald-700"
|
:class "px-3 py-1 bg-emerald-600 text-white rounded text-sm hover:bg-emerald-700"
|
||||||
@@ -141,13 +141,13 @@
|
|||||||
(defcomp ~ref-swap-demo ()
|
(defcomp ~ref-swap-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(div :class "flex gap-2 flex-wrap"
|
(div :class "flex gap-2 flex-wrap"
|
||||||
(button :sx-get "/(geography.(hypermedia.(reference.(api.swap-item))))"
|
(button :sx-get "/sx/(geography.(hypermedia.(reference.(api.swap-item))))"
|
||||||
:sx-target "#ref-swap-list" :sx-swap "beforeend"
|
:sx-target "#ref-swap-list" :sx-swap "beforeend"
|
||||||
:class "px-3 py-1 bg-violet-600 text-white rounded text-sm" "beforeend")
|
:class "px-3 py-1 bg-violet-600 text-white rounded text-sm" "beforeend")
|
||||||
(button :sx-get "/(geography.(hypermedia.(reference.(api.swap-item))))"
|
(button :sx-get "/sx/(geography.(hypermedia.(reference.(api.swap-item))))"
|
||||||
:sx-target "#ref-swap-list" :sx-swap "afterbegin"
|
:sx-target "#ref-swap-list" :sx-swap "afterbegin"
|
||||||
:class "px-3 py-1 bg-emerald-600 text-white rounded text-sm" "afterbegin")
|
:class "px-3 py-1 bg-emerald-600 text-white rounded text-sm" "afterbegin")
|
||||||
(button :sx-get "/(geography.(hypermedia.(reference.(api.swap-item))))"
|
(button :sx-get "/sx/(geography.(hypermedia.(reference.(api.swap-item))))"
|
||||||
:sx-target "#ref-swap-list" :sx-swap "innerHTML"
|
:sx-target "#ref-swap-list" :sx-swap "innerHTML"
|
||||||
:class "px-3 py-1 bg-blue-600 text-white rounded text-sm" "innerHTML"))
|
:class "px-3 py-1 bg-blue-600 text-white rounded text-sm" "innerHTML"))
|
||||||
(div :id "ref-swap-list"
|
(div :id "ref-swap-list"
|
||||||
@@ -160,7 +160,7 @@
|
|||||||
|
|
||||||
(defcomp ~ref-oob-demo ()
|
(defcomp ~ref-oob-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(button :sx-get "/(geography.(hypermedia.(reference.(api.oob))))"
|
(button :sx-get "/sx/(geography.(hypermedia.(reference.(api.oob))))"
|
||||||
:sx-target "#ref-oob-main"
|
:sx-target "#ref-oob-main"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "px-4 py-2 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
:class "px-4 py-2 bg-violet-600 text-white rounded text-sm hover:bg-violet-700"
|
||||||
@@ -179,7 +179,7 @@
|
|||||||
|
|
||||||
(defcomp ~ref-select-demo ()
|
(defcomp ~ref-select-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(button :sx-get "/(geography.(hypermedia.(reference.(api.select-page))))"
|
(button :sx-get "/sx/(geography.(hypermedia.(reference.(api.select-page))))"
|
||||||
:sx-target "#ref-select-result"
|
:sx-target "#ref-select-result"
|
||||||
:sx-select "#the-content"
|
:sx-select "#the-content"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
@@ -198,7 +198,7 @@
|
|||||||
(div :id "ref-confirm-item"
|
(div :id "ref-confirm-item"
|
||||||
:class "flex items-center justify-between p-3 border border-stone-200 rounded"
|
:class "flex items-center justify-between p-3 border border-stone-200 rounded"
|
||||||
(span :class "text-sm text-stone-700" "Important file.txt")
|
(span :class "text-sm text-stone-700" "Important file.txt")
|
||||||
(button :sx-delete "/(geography.(hypermedia.(reference.(api.(item.confirm)))))"
|
(button :sx-delete "/sx/(geography.(hypermedia.(reference.(api.(item.confirm)))))"
|
||||||
:sx-target "#ref-confirm-item" :sx-swap "delete"
|
:sx-target "#ref-confirm-item" :sx-swap "delete"
|
||||||
:sx-confirm "Are you sure you want to delete this file?"
|
:sx-confirm "Are you sure you want to delete this file?"
|
||||||
:class "px-3 py-1 text-red-500 text-sm border border-red-200 rounded hover:bg-red-50"
|
:class "px-3 py-1 text-red-500 text-sm border border-red-200 rounded hover:bg-red-50"
|
||||||
@@ -211,14 +211,14 @@
|
|||||||
(defcomp ~ref-pushurl-demo ()
|
(defcomp ~ref-pushurl-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(div :class "flex gap-2"
|
(div :class "flex gap-2"
|
||||||
(a :href "/(geography.(hypermedia.(reference-detail.attributes.sx-get)))"
|
(a :href "/sx/(geography.(hypermedia.(reference-detail.attributes.sx-get)))"
|
||||||
:sx-get "/(geography.(hypermedia.(reference-detail.attributes.sx-get)))"
|
:sx-get "/sx/(geography.(hypermedia.(reference-detail.attributes.sx-get)))"
|
||||||
:sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
:class "px-3 py-1 bg-violet-100 text-violet-700 rounded text-sm no-underline hover:bg-violet-200"
|
:class "px-3 py-1 bg-violet-100 text-violet-700 rounded text-sm no-underline hover:bg-violet-200"
|
||||||
"sx-get page")
|
"sx-get page")
|
||||||
(a :href "/(geography.(hypermedia.(reference-detail.attributes.sx-post)))"
|
(a :href "/sx/(geography.(hypermedia.(reference-detail.attributes.sx-post)))"
|
||||||
:sx-get "/(geography.(hypermedia.(reference-detail.attributes.sx-post)))"
|
:sx-get "/sx/(geography.(hypermedia.(reference-detail.attributes.sx-post)))"
|
||||||
:sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
:class "px-3 py-1 bg-violet-100 text-violet-700 rounded text-sm no-underline hover:bg-violet-200"
|
:class "px-3 py-1 bg-violet-100 text-violet-700 rounded text-sm no-underline hover:bg-violet-200"
|
||||||
@@ -233,7 +233,7 @@
|
|||||||
(defcomp ~ref-sync-demo ()
|
(defcomp ~ref-sync-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(input :type "text" :name "q" :placeholder "Type quickly..."
|
(input :type "text" :name "q" :placeholder "Type quickly..."
|
||||||
:sx-get "/(geography.(hypermedia.(reference.(api.slow-echo))))"
|
:sx-get "/sx/(geography.(hypermedia.(reference.(api.slow-echo))))"
|
||||||
:sx-trigger "input changed delay:100ms"
|
:sx-trigger "input changed delay:100ms"
|
||||||
:sx-sync "replace"
|
:sx-sync "replace"
|
||||||
:sx-target "#ref-sync-result"
|
:sx-target "#ref-sync-result"
|
||||||
@@ -251,7 +251,7 @@
|
|||||||
|
|
||||||
(defcomp ~ref-encoding-demo ()
|
(defcomp ~ref-encoding-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(form :sx-post "/(geography.(hypermedia.(reference.(api.upload-name))))"
|
(form :sx-post "/sx/(geography.(hypermedia.(reference.(api.upload-name))))"
|
||||||
:sx-encoding "multipart/form-data"
|
:sx-encoding "multipart/form-data"
|
||||||
:sx-target "#ref-encoding-result"
|
:sx-target "#ref-encoding-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
@@ -271,7 +271,7 @@
|
|||||||
|
|
||||||
(defcomp ~ref-headers-demo ()
|
(defcomp ~ref-headers-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(button :sx-get "/(geography.(hypermedia.(reference.(api.echo-headers))))"
|
(button :sx-get "/sx/(geography.(hypermedia.(reference.(api.echo-headers))))"
|
||||||
:sx-headers {:X-Custom-Token "abc123" :X-Request-Source "demo"}
|
:sx-headers {:X-Custom-Token "abc123" :X-Request-Source "demo"}
|
||||||
:sx-target "#ref-headers-result"
|
:sx-target "#ref-headers-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
@@ -295,7 +295,7 @@
|
|||||||
(option :value "all" "All")
|
(option :value "all" "All")
|
||||||
(option :value "books" "Books")
|
(option :value "books" "Books")
|
||||||
(option :value "tools" "Tools")))
|
(option :value "tools" "Tools")))
|
||||||
(button :sx-get "/(geography.(hypermedia.(reference.(api.echo-vals))))"
|
(button :sx-get "/sx/(geography.(hypermedia.(reference.(api.echo-vals))))"
|
||||||
:sx-include "#ref-inc-cat"
|
:sx-include "#ref-inc-cat"
|
||||||
:sx-target "#ref-include-result"
|
:sx-target "#ref-include-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
@@ -311,7 +311,7 @@
|
|||||||
|
|
||||||
(defcomp ~ref-vals-demo ()
|
(defcomp ~ref-vals-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(button :sx-post "/(geography.(hypermedia.(reference.(api.echo-vals))))"
|
(button :sx-post "/sx/(geography.(hypermedia.(reference.(api.echo-vals))))"
|
||||||
:sx-vals "{\"source\": \"demo\", \"page\": \"3\"}"
|
:sx-vals "{\"source\": \"demo\", \"page\": \"3\"}"
|
||||||
:sx-target "#ref-vals-result"
|
:sx-target "#ref-vals-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
@@ -327,8 +327,8 @@
|
|||||||
|
|
||||||
(defcomp ~ref-media-demo ()
|
(defcomp ~ref-media-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(a :href "/(geography.(hypermedia.(reference-detail.attributes.sx-get)))"
|
(a :href "/sx/(geography.(hypermedia.(reference-detail.attributes.sx-get)))"
|
||||||
:sx-get "/(geography.(hypermedia.(reference-detail.attributes.sx-get)))"
|
:sx-get "/sx/(geography.(hypermedia.(reference-detail.attributes.sx-get)))"
|
||||||
:sx-target "#main-panel" :sx-select "#main-panel"
|
:sx-target "#main-panel" :sx-select "#main-panel"
|
||||||
:sx-swap "outerHTML" :sx-push-url "true"
|
:sx-swap "outerHTML" :sx-push-url "true"
|
||||||
:sx-media "(min-width: 768px)"
|
:sx-media "(min-width: 768px)"
|
||||||
@@ -346,13 +346,13 @@
|
|||||||
(div :class "grid grid-cols-2 gap-3"
|
(div :class "grid grid-cols-2 gap-3"
|
||||||
(div :class "p-3 border border-stone-200 rounded"
|
(div :class "p-3 border border-stone-200 rounded"
|
||||||
(p :class "text-xs text-stone-400 mb-2" "sx enabled")
|
(p :class "text-xs text-stone-400 mb-2" "sx enabled")
|
||||||
(button :sx-get "/(geography.(hypermedia.(reference.(api.time))))"
|
(button :sx-get "/sx/(geography.(hypermedia.(reference.(api.time))))"
|
||||||
:sx-target "#ref-dis-a" :sx-swap "innerHTML"
|
:sx-target "#ref-dis-a" :sx-swap "innerHTML"
|
||||||
:class "px-3 py-1 bg-violet-600 text-white rounded text-sm" "Load")
|
:class "px-3 py-1 bg-violet-600 text-white rounded text-sm" "Load")
|
||||||
(div :id "ref-dis-a" :class "mt-2 text-sm text-stone-500" "—"))
|
(div :id "ref-dis-a" :class "mt-2 text-sm text-stone-500" "—"))
|
||||||
(div :sx-disable "true" :class "p-3 border border-stone-200 rounded"
|
(div :sx-disable "true" :class "p-3 border border-stone-200 rounded"
|
||||||
(p :class "text-xs text-stone-400 mb-2" "sx disabled")
|
(p :class "text-xs text-stone-400 mb-2" "sx disabled")
|
||||||
(button :sx-get "/(geography.(hypermedia.(reference.(api.time))))"
|
(button :sx-get "/sx/(geography.(hypermedia.(reference.(api.time))))"
|
||||||
:sx-target "#ref-dis-b" :sx-swap "innerHTML"
|
:sx-target "#ref-dis-b" :sx-swap "innerHTML"
|
||||||
:class "px-3 py-1 bg-stone-400 text-white rounded text-sm" "Load")
|
:class "px-3 py-1 bg-stone-400 text-white rounded text-sm" "Load")
|
||||||
(div :id "ref-dis-b" :class "mt-2 text-sm text-stone-500"
|
(div :id "ref-dis-b" :class "mt-2 text-sm text-stone-500"
|
||||||
@@ -378,7 +378,7 @@
|
|||||||
|
|
||||||
(defcomp ~ref-retry-demo ()
|
(defcomp ~ref-retry-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(button :sx-get "/(geography.(hypermedia.(reference.(api.flaky))))"
|
(button :sx-get "/sx/(geography.(hypermedia.(reference.(api.flaky))))"
|
||||||
:sx-target "#ref-retry-result"
|
:sx-target "#ref-retry-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-retry "true"
|
:sx-retry "true"
|
||||||
@@ -414,13 +414,13 @@
|
|||||||
(defcomp ~ref-boost-demo ()
|
(defcomp ~ref-boost-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(nav :sx-boost "true" :class "flex gap-3"
|
(nav :sx-boost "true" :class "flex gap-3"
|
||||||
(a :href "/(geography.(hypermedia.(reference-detail.attributes.sx-get)))"
|
(a :href "/sx/(geography.(hypermedia.(reference-detail.attributes.sx-get)))"
|
||||||
:class "text-violet-600 hover:text-violet-800 underline text-sm"
|
:class "text-violet-600 hover:text-violet-800 underline text-sm"
|
||||||
"sx-get")
|
"sx-get")
|
||||||
(a :href "/(geography.(hypermedia.(reference-detail.attributes.sx-post)))"
|
(a :href "/sx/(geography.(hypermedia.(reference-detail.attributes.sx-post)))"
|
||||||
:class "text-violet-600 hover:text-violet-800 underline text-sm"
|
:class "text-violet-600 hover:text-violet-800 underline text-sm"
|
||||||
"sx-post")
|
"sx-post")
|
||||||
(a :href "/(geography.(hypermedia.(reference-detail.attributes.sx-target)))"
|
(a :href "/sx/(geography.(hypermedia.(reference-detail.attributes.sx-target)))"
|
||||||
:class "text-violet-600 hover:text-violet-800 underline text-sm"
|
:class "text-violet-600 hover:text-violet-800 underline text-sm"
|
||||||
"sx-target"))
|
"sx-target"))
|
||||||
(p :class "text-xs text-stone-400"
|
(p :class "text-xs text-stone-400"
|
||||||
@@ -434,7 +434,7 @@
|
|||||||
(defcomp ~ref-preload-demo ()
|
(defcomp ~ref-preload-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(reference.(api.time))))"
|
:sx-get "/sx/(geography.(hypermedia.(reference.(api.time))))"
|
||||||
:sx-target "#ref-preload-result"
|
:sx-target "#ref-preload-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-preload "mouseover"
|
:sx-preload "mouseover"
|
||||||
@@ -452,7 +452,7 @@
|
|||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(div :class "flex gap-2 items-center"
|
(div :class "flex gap-2 items-center"
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(reference.(api.time))))"
|
:sx-get "/sx/(geography.(hypermedia.(reference.(api.time))))"
|
||||||
:sx-target "#ref-preserve-container"
|
:sx-target "#ref-preserve-container"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
||||||
@@ -473,7 +473,7 @@
|
|||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(div :class "flex gap-3 items-center"
|
(div :class "flex gap-3 items-center"
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(reference.(api.slow-echo))))"
|
:sx-get "/sx/(geography.(hypermedia.(reference.(api.slow-echo))))"
|
||||||
:sx-target "#ref-indicator-result"
|
:sx-target "#ref-indicator-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-indicator "#ref-spinner"
|
:sx-indicator "#ref-spinner"
|
||||||
@@ -495,7 +495,7 @@
|
|||||||
(defcomp ~ref-validate-demo ()
|
(defcomp ~ref-validate-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(form
|
(form
|
||||||
:sx-post "/(geography.(hypermedia.(reference.(api.greet))))"
|
:sx-post "/sx/(geography.(hypermedia.(reference.(api.greet))))"
|
||||||
:sx-target "#ref-validate-result"
|
:sx-target "#ref-validate-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-validate "true"
|
:sx-validate "true"
|
||||||
@@ -517,7 +517,7 @@
|
|||||||
(defcomp ~ref-ignore-demo ()
|
(defcomp ~ref-ignore-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(reference.(api.time))))"
|
:sx-get "/sx/(geography.(hypermedia.(reference.(api.time))))"
|
||||||
:sx-target "#ref-ignore-container"
|
:sx-target "#ref-ignore-container"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
||||||
@@ -539,14 +539,14 @@
|
|||||||
(div :id "ref-opt-item-1"
|
(div :id "ref-opt-item-1"
|
||||||
:class "flex items-center justify-between p-2 border border-stone-200 rounded"
|
:class "flex items-center justify-between p-2 border border-stone-200 rounded"
|
||||||
(span :class "text-sm text-stone-700" "Optimistic item A")
|
(span :class "text-sm text-stone-700" "Optimistic item A")
|
||||||
(button :sx-delete "/(geography.(hypermedia.(reference.(api.(item.opt1)))))"
|
(button :sx-delete "/sx/(geography.(hypermedia.(reference.(api.(item.opt1)))))"
|
||||||
:sx-target "#ref-opt-item-1" :sx-swap "delete"
|
:sx-target "#ref-opt-item-1" :sx-swap "delete"
|
||||||
:sx-optimistic "remove"
|
:sx-optimistic "remove"
|
||||||
:class "text-red-500 text-sm hover:text-red-700" "Remove"))
|
:class "text-red-500 text-sm hover:text-red-700" "Remove"))
|
||||||
(div :id "ref-opt-item-2"
|
(div :id "ref-opt-item-2"
|
||||||
:class "flex items-center justify-between p-2 border border-stone-200 rounded"
|
:class "flex items-center justify-between p-2 border border-stone-200 rounded"
|
||||||
(span :class "text-sm text-stone-700" "Optimistic item B")
|
(span :class "text-sm text-stone-700" "Optimistic item B")
|
||||||
(button :sx-delete "/(geography.(hypermedia.(reference.(api.(item.opt2)))))"
|
(button :sx-delete "/sx/(geography.(hypermedia.(reference.(api.(item.opt2)))))"
|
||||||
:sx-target "#ref-opt-item-2" :sx-swap "delete"
|
:sx-target "#ref-opt-item-2" :sx-swap "delete"
|
||||||
:sx-optimistic "remove"
|
:sx-optimistic "remove"
|
||||||
:class "text-red-500 text-sm hover:text-red-700" "Remove"))
|
:class "text-red-500 text-sm hover:text-red-700" "Remove"))
|
||||||
@@ -560,7 +560,7 @@
|
|||||||
(defcomp ~ref-replace-url-demo ()
|
(defcomp ~ref-replace-url-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(reference.(api.time))))"
|
:sx-get "/sx/(geography.(hypermedia.(reference.(api.time))))"
|
||||||
:sx-target "#ref-replurl-result"
|
:sx-target "#ref-replurl-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-replace-url "true"
|
:sx-replace-url "true"
|
||||||
@@ -578,7 +578,7 @@
|
|||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(div :class "flex gap-3 items-center"
|
(div :class "flex gap-3 items-center"
|
||||||
(button :id "ref-diselt-btn"
|
(button :id "ref-diselt-btn"
|
||||||
:sx-get "/(geography.(hypermedia.(reference.(api.slow-echo))))"
|
:sx-get "/sx/(geography.(hypermedia.(reference.(api.slow-echo))))"
|
||||||
:sx-target "#ref-diselt-result"
|
:sx-target "#ref-diselt-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-disabled-elt "#ref-diselt-btn"
|
:sx-disabled-elt "#ref-diselt-btn"
|
||||||
@@ -597,7 +597,7 @@
|
|||||||
(defcomp ~ref-prompt-demo ()
|
(defcomp ~ref-prompt-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(reference.(api.prompt-echo))))"
|
:sx-get "/sx/(geography.(hypermedia.(reference.(api.prompt-echo))))"
|
||||||
:sx-target "#ref-prompt-result"
|
:sx-target "#ref-prompt-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-prompt "Enter your name:"
|
:sx-prompt "Enter your name:"
|
||||||
@@ -614,7 +614,7 @@
|
|||||||
(defcomp ~ref-params-demo ()
|
(defcomp ~ref-params-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(form
|
(form
|
||||||
:sx-post "/(geography.(hypermedia.(reference.(api.echo-vals))))"
|
:sx-post "/sx/(geography.(hypermedia.(reference.(api.echo-vals))))"
|
||||||
:sx-target "#ref-params-result"
|
:sx-target "#ref-params-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-params "name"
|
:sx-params "name"
|
||||||
@@ -636,7 +636,7 @@
|
|||||||
|
|
||||||
(defcomp ~ref-sse-demo ()
|
(defcomp ~ref-sse-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(div :sx-sse "/(geography.(hypermedia.(reference.(api.sse-time))))"
|
(div :sx-sse "/sx/(geography.(hypermedia.(reference.(api.sse-time))))"
|
||||||
:sx-sse-swap "time"
|
:sx-sse-swap "time"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
(div :id "ref-sse-result"
|
(div :id "ref-sse-result"
|
||||||
@@ -656,7 +656,7 @@
|
|||||||
(defcomp ~ref-header-prompt-demo ()
|
(defcomp ~ref-header-prompt-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(reference.(api.prompt-echo))))"
|
:sx-get "/sx/(geography.(hypermedia.(reference.(api.prompt-echo))))"
|
||||||
:sx-target "#ref-hdr-prompt-result"
|
:sx-target "#ref-hdr-prompt-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-prompt "Enter your name:"
|
:sx-prompt "Enter your name:"
|
||||||
@@ -673,7 +673,7 @@
|
|||||||
(defcomp ~ref-header-trigger-demo ()
|
(defcomp ~ref-header-trigger-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(reference.(api.trigger-event))))"
|
:sx-get "/sx/(geography.(hypermedia.(reference.(api.trigger-event))))"
|
||||||
:sx-target "#ref-hdr-trigger-result"
|
:sx-target "#ref-hdr-trigger-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
||||||
@@ -690,7 +690,7 @@
|
|||||||
(defcomp ~ref-header-retarget-demo ()
|
(defcomp ~ref-header-retarget-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(reference.(api.retarget))))"
|
:sx-get "/sx/(geography.(hypermedia.(reference.(api.retarget))))"
|
||||||
:sx-target "#ref-hdr-retarget-main"
|
:sx-target "#ref-hdr-retarget-main"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
:class "px-4 py-2 bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors text-sm"
|
||||||
@@ -717,7 +717,7 @@
|
|||||||
(input :id "ref-evt-br-input" :type "text" :placeholder "Type something first..."
|
(input :id "ref-evt-br-input" :type "text" :placeholder "Type something first..."
|
||||||
:class "flex-1 px-3 py-2 border border-stone-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-violet-500")
|
:class "flex-1 px-3 py-2 border border-stone-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-violet-500")
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(reference.(api.time))))"
|
:sx-get "/sx/(geography.(hypermedia.(reference.(api.time))))"
|
||||||
:sx-target "#ref-evt-br-result"
|
:sx-target "#ref-evt-br-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-on:sx:beforeRequest "if (!document.getElementById('ref-evt-br-input').value) { event.preventDefault(); document.getElementById('ref-evt-br-result').textContent = 'Cancelled — input is empty!'; }"
|
:sx-on:sx:beforeRequest "if (!document.getElementById('ref-evt-br-input').value) { event.preventDefault(); document.getElementById('ref-evt-br-result').textContent = 'Cancelled — input is empty!'; }"
|
||||||
@@ -734,7 +734,7 @@
|
|||||||
(defcomp ~ref-event-after-request-demo ()
|
(defcomp ~ref-event-after-request-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(reference.(api.time))))"
|
:sx-get "/sx/(geography.(hypermedia.(reference.(api.time))))"
|
||||||
:sx-target "#ref-evt-ar-result"
|
:sx-target "#ref-evt-ar-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-on:sx:afterRequest "document.getElementById('ref-evt-ar-log').textContent = 'Response status: ' + (event.detail ? event.detail.status : '?')"
|
:sx-on:sx:afterRequest "document.getElementById('ref-evt-ar-log').textContent = 'Response status: ' + (event.detail ? event.detail.status : '?')"
|
||||||
@@ -754,7 +754,7 @@
|
|||||||
(defcomp ~ref-event-after-swap-demo ()
|
(defcomp ~ref-event-after-swap-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(reference.(api.swap-item))))"
|
:sx-get "/sx/(geography.(hypermedia.(reference.(api.swap-item))))"
|
||||||
:sx-target "#ref-evt-as-list"
|
:sx-target "#ref-evt-as-list"
|
||||||
:sx-swap "beforeend"
|
:sx-swap "beforeend"
|
||||||
:sx-on:sx:afterSwap "var items = document.querySelectorAll('#ref-evt-as-list > div'); if (items.length) items[items.length-1].scrollIntoView({behavior:'smooth'}); document.getElementById('ref-evt-as-count').textContent = items.length + ' items'"
|
:sx-on:sx:afterSwap "var items = document.querySelectorAll('#ref-evt-as-list > div'); if (items.length) items[items.length-1].scrollIntoView({behavior:'smooth'}); document.getElementById('ref-evt-as-count').textContent = items.length + ' items'"
|
||||||
@@ -774,7 +774,7 @@
|
|||||||
(defcomp ~ref-event-response-error-demo ()
|
(defcomp ~ref-event-response-error-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(button
|
(button
|
||||||
:sx-get "/(geography.(hypermedia.(reference.(api.error-500))))"
|
:sx-get "/sx/(geography.(hypermedia.(reference.(api.error-500))))"
|
||||||
:sx-target "#ref-evt-err-result"
|
:sx-target "#ref-evt-err-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-on:sx:responseError "var s=document.getElementById('ref-evt-err-status'); s.style.display='block'; s.textContent='Error ' + (event.detail ? event.detail.status || '?' : '?') + ' received'"
|
:sx-on:sx:responseError "var s=document.getElementById('ref-evt-err-status'); s.style.display='block'; s.textContent='Error ' + (event.detail ? event.detail.status || '?' : '?') + ' received'"
|
||||||
@@ -796,7 +796,7 @@
|
|||||||
(defcomp ~ref-event-validation-failed-demo ()
|
(defcomp ~ref-event-validation-failed-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(form
|
(form
|
||||||
:sx-post "/(geography.(hypermedia.(reference.(api.greet))))"
|
:sx-post "/sx/(geography.(hypermedia.(reference.(api.greet))))"
|
||||||
:sx-target "#ref-evt-vf-result"
|
:sx-target "#ref-evt-vf-result"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-validate "true"
|
:sx-validate "true"
|
||||||
@@ -847,13 +847,13 @@
|
|||||||
"Open DevTools console, then navigate to a pure page (no :data expression). "
|
"Open DevTools console, then navigate to a pure page (no :data expression). "
|
||||||
"You'll see \"sx:route client /path\" in the console — no network request is made.")
|
"You'll see \"sx:route client /path\" in the console — no network request is made.")
|
||||||
(div :class "flex gap-2 flex-wrap"
|
(div :class "flex gap-2 flex-wrap"
|
||||||
(a :href "/(etc.(essay))"
|
(a :href "/sx/(etc.(essay))"
|
||||||
:class "px-3 py-1 bg-violet-100 text-violet-700 rounded text-sm no-underline hover:bg-violet-200"
|
:class "px-3 py-1 bg-violet-100 text-violet-700 rounded text-sm no-underline hover:bg-violet-200"
|
||||||
"Essays")
|
"Essays")
|
||||||
(a :href "/(etc.(plan))"
|
(a :href "/sx/(etc.(plan))"
|
||||||
:class "px-3 py-1 bg-violet-100 text-violet-700 rounded text-sm no-underline hover:bg-violet-200"
|
:class "px-3 py-1 bg-violet-100 text-violet-700 rounded text-sm no-underline hover:bg-violet-200"
|
||||||
"Plans")
|
"Plans")
|
||||||
(a :href "/(applications.(protocol))"
|
(a :href "/sx/(applications.(protocol))"
|
||||||
:class "px-3 py-1 bg-violet-100 text-violet-700 rounded text-sm no-underline hover:bg-violet-200"
|
:class "px-3 py-1 bg-violet-100 text-violet-700 rounded text-sm no-underline hover:bg-violet-200"
|
||||||
"Protocols"))
|
"Protocols"))
|
||||||
(p :class "text-xs text-stone-400"
|
(p :class "text-xs text-stone-400"
|
||||||
@@ -866,7 +866,7 @@
|
|||||||
|
|
||||||
(defcomp ~ref-event-sse-open-demo ()
|
(defcomp ~ref-event-sse-open-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(div :sx-sse "/(geography.(hypermedia.(reference.(api.sse-time))))"
|
(div :sx-sse "/sx/(geography.(hypermedia.(reference.(api.sse-time))))"
|
||||||
:sx-sse-swap "time"
|
:sx-sse-swap "time"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-on:sx:sseOpen "document.getElementById('ref-evt-sseopen-status').textContent = 'Connected'; document.getElementById('ref-evt-sseopen-status').className = 'inline-block px-2 py-0.5 rounded text-xs bg-emerald-100 text-emerald-700'"
|
:sx-on:sx:sseOpen "document.getElementById('ref-evt-sseopen-status').textContent = 'Connected'; document.getElementById('ref-evt-sseopen-status').className = 'inline-block px-2 py-0.5 rounded text-xs bg-emerald-100 text-emerald-700'"
|
||||||
@@ -884,7 +884,7 @@
|
|||||||
|
|
||||||
(defcomp ~ref-event-sse-message-demo ()
|
(defcomp ~ref-event-sse-message-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(div :sx-sse "/(geography.(hypermedia.(reference.(api.sse-time))))"
|
(div :sx-sse "/sx/(geography.(hypermedia.(reference.(api.sse-time))))"
|
||||||
:sx-sse-swap "time"
|
:sx-sse-swap "time"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-on:sx:sseMessage "var c = parseInt(document.getElementById('ref-evt-ssemsg-count').dataset.count || '0') + 1; document.getElementById('ref-evt-ssemsg-count').dataset.count = c; document.getElementById('ref-evt-ssemsg-count').textContent = c + ' messages received'"
|
:sx-on:sx:sseMessage "var c = parseInt(document.getElementById('ref-evt-ssemsg-count').dataset.count || '0') + 1; document.getElementById('ref-evt-ssemsg-count').dataset.count = c; document.getElementById('ref-evt-ssemsg-count').textContent = c + ' messages received'"
|
||||||
@@ -902,7 +902,7 @@
|
|||||||
|
|
||||||
(defcomp ~ref-event-sse-error-demo ()
|
(defcomp ~ref-event-sse-error-demo ()
|
||||||
(div :class "space-y-3"
|
(div :class "space-y-3"
|
||||||
(div :sx-sse "/(geography.(hypermedia.(reference.(api.sse-time))))"
|
(div :sx-sse "/sx/(geography.(hypermedia.(reference.(api.sse-time))))"
|
||||||
:sx-sse-swap "time"
|
:sx-sse-swap "time"
|
||||||
:sx-swap "innerHTML"
|
:sx-swap "innerHTML"
|
||||||
:sx-on:sx:sseError "document.getElementById('ref-evt-sseerr-status').textContent = 'Disconnected'; document.getElementById('ref-evt-sseerr-status').className = 'inline-block px-2 py-0.5 rounded text-xs bg-red-100 text-red-700'"
|
:sx-on:sx:sseError "document.getElementById('ref-evt-sseerr-status').textContent = 'Disconnected'; document.getElementById('ref-evt-sseerr-status').className = 'inline-block px-2 py-0.5 rounded text-xs bg-red-100 text-red-700'"
|
||||||
|
|||||||
Reference in New Issue
Block a user