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:
@@ -17,7 +17,7 @@
|
||||
"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:")
|
||||
(~doc-code :code (highlight
|
||||
"/(applications.(sx-urls))"
|
||||
"/sx/(applications.(sx-urls))"
|
||||
"lisp"))
|
||||
(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, "
|
||||
@@ -73,13 +73,13 @@
|
||||
"Called with no arguments, they return their index page. "
|
||||
"Called with content, they pass it through:")
|
||||
(~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")))
|
||||
|
||||
(~doc-subsection :title "Sub-sections"
|
||||
(p "Sub-sections nest inside sections:")
|
||||
(~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")))
|
||||
|
||||
(~doc-subsection :title "Leaf pages"
|
||||
@@ -104,7 +104,7 @@
|
||||
"lisp"))
|
||||
(p "New components are URL-accessible the moment they are defined. "
|
||||
"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"
|
||||
@@ -186,27 +186,27 @@
|
||||
(tbody :class "text-stone-600"
|
||||
(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-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"))
|
||||
(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-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"))
|
||||
(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-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"))
|
||||
(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-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"))
|
||||
(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-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"))
|
||||
(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-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")))))
|
||||
|
||||
(p "SX has four sigils, each marking a different kind of name:")
|
||||
@@ -224,21 +224,21 @@
|
||||
(~doc-subsection :title "Links"
|
||||
(p "Standard anchor tags with SX URL hrefs:")
|
||||
(~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")))
|
||||
|
||||
(~doc-subsection :title "sx-get — HTMX-style fetching"
|
||||
(p (code "sx-get") " fetches content from an SX URL and swaps it into the DOM. "
|
||||
"SX URLs work exactly like path URLs:")
|
||||
(~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")))
|
||||
|
||||
(~doc-subsection :title "sx-boost — progressive enhancement"
|
||||
(p (code "sx-boost") " upgrades regular links to use HTMX-style fetch+swap "
|
||||
"instead of full-page navigation:")
|
||||
(~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")))
|
||||
|
||||
(~doc-subsection :title "Pagination pattern"
|
||||
@@ -265,7 +265,7 @@
|
||||
(~doc-subsection :title "Step 1: Parse"
|
||||
(p "Strip the leading " (code "/") ", replace dots with spaces, parse as SX:")
|
||||
(~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")))
|
||||
|
||||
(~doc-subsection :title "Step 2: Auto-quote"
|
||||
@@ -386,7 +386,7 @@
|
||||
"This means:")
|
||||
(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 "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 "Indexable") " — crawlers follow " (code "<a href>") " links")
|
||||
(li (strong "No client library") " — " (code "curl '/(language.(doc.intro))'") " returns content"))
|
||||
@@ -398,17 +398,17 @@
|
||||
;; -----------------------------------------------------------------
|
||||
(~doc-section :title "The Router Spec" :id "spec"
|
||||
(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, "
|
||||
"and special form detection as pure functions.")
|
||||
|
||||
(p "Key functions from the spec:")
|
||||
(~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"))
|
||||
|
||||
(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, "
|
||||
"delta values, special form parsing, and bare-dot shorthand. "
|
||||
"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"
|
||||
(p "The address bar is the input line. The page is the output.")
|
||||
(~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"))
|
||||
(p "You do not need to explain what SX is. "
|
||||
"Show someone a URL, and they immediately understand the philosophy. "
|
||||
|
||||
Reference in New Issue
Block a user