URL restructure, 404 page, trailing slash normalization, layout fixes

- Rename /reactive-islands/ → /reactive/, /reference/ → /hypermedia/reference/,
  /examples/ → /hypermedia/examples/ across all .sx and .py files
- Add 404 error page (not-found.sx) working on both server refresh and
  client-side SX navigation via orchestration.sx error response handling
- Add trailing slash redirect (GET only, excludes /api/, /static/, /internal/)
- Remove blue sky-500 header bar from SX docs layout (conditional on header-rows)
- Fix 405 on API endpoints from trailing slash redirect hitting POST/PUT/DELETE
- Fix client-side 404: orchestration.sx now swaps error response content
  instead of silently dropping it
- Add new plan files and home page component

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-10 21:30:18 +00:00
parent e149dfe968
commit 1341c144da
35 changed files with 2305 additions and 438 deletions

View File

@@ -20,10 +20,10 @@ DOCS_NAV = [
]
REFERENCE_NAV = [
("Attributes", "/reference/attributes"),
("Headers", "/reference/headers"),
("Events", "/reference/events"),
("JS API", "/reference/js-api"),
("Attributes", "/hypermedia/reference/attributes"),
("Headers", "/hypermedia/reference/headers"),
("Events", "/hypermedia/reference/events"),
("JS API", "/hypermedia/reference/js-api"),
]
PROTOCOLS_NAV = [
@@ -36,33 +36,33 @@ PROTOCOLS_NAV = [
]
EXAMPLES_NAV = [
("Click to Load", "/examples/click-to-load"),
("Form Submission", "/examples/form-submission"),
("Polling", "/examples/polling"),
("Delete Row", "/examples/delete-row"),
("Inline Edit", "/examples/inline-edit"),
("OOB Swaps", "/examples/oob-swaps"),
("Lazy Loading", "/examples/lazy-loading"),
("Infinite Scroll", "/examples/infinite-scroll"),
("Progress Bar", "/examples/progress-bar"),
("Active Search", "/examples/active-search"),
("Inline Validation", "/examples/inline-validation"),
("Value Select", "/examples/value-select"),
("Reset on Submit", "/examples/reset-on-submit"),
("Edit Row", "/examples/edit-row"),
("Bulk Update", "/examples/bulk-update"),
("Swap Positions", "/examples/swap-positions"),
("Select Filter", "/examples/select-filter"),
("Tabs", "/examples/tabs"),
("Animations", "/examples/animations"),
("Dialogs", "/examples/dialogs"),
("Keyboard Shortcuts", "/examples/keyboard-shortcuts"),
("PUT / PATCH", "/examples/put-patch"),
("JSON Encoding", "/examples/json-encoding"),
("Vals & Headers", "/examples/vals-and-headers"),
("Loading States", "/examples/loading-states"),
("Request Abort", "/examples/sync-replace"),
("Retry", "/examples/retry"),
("Click to Load", "/hypermedia/examples/click-to-load"),
("Form Submission", "/hypermedia/examples/form-submission"),
("Polling", "/hypermedia/examples/polling"),
("Delete Row", "/hypermedia/examples/delete-row"),
("Inline Edit", "/hypermedia/examples/inline-edit"),
("OOB Swaps", "/hypermedia/examples/oob-swaps"),
("Lazy Loading", "/hypermedia/examples/lazy-loading"),
("Infinite Scroll", "/hypermedia/examples/infinite-scroll"),
("Progress Bar", "/hypermedia/examples/progress-bar"),
("Active Search", "/hypermedia/examples/active-search"),
("Inline Validation", "/hypermedia/examples/inline-validation"),
("Value Select", "/hypermedia/examples/value-select"),
("Reset on Submit", "/hypermedia/examples/reset-on-submit"),
("Edit Row", "/hypermedia/examples/edit-row"),
("Bulk Update", "/hypermedia/examples/bulk-update"),
("Swap Positions", "/hypermedia/examples/swap-positions"),
("Select Filter", "/hypermedia/examples/select-filter"),
("Tabs", "/hypermedia/examples/tabs"),
("Animations", "/hypermedia/examples/animations"),
("Dialogs", "/hypermedia/examples/dialogs"),
("Keyboard Shortcuts", "/hypermedia/examples/keyboard-shortcuts"),
("PUT / PATCH", "/hypermedia/examples/put-patch"),
("JSON Encoding", "/hypermedia/examples/json-encoding"),
("Vals & Headers", "/hypermedia/examples/vals-and-headers"),
("Loading States", "/hypermedia/examples/loading-states"),
("Request Abort", "/hypermedia/examples/sync-replace"),
("Retry", "/hypermedia/examples/retry"),
]
ESSAYS_NAV = [
@@ -79,9 +79,9 @@ ESSAYS_NAV = [
MAIN_NAV = [
("Docs", "/docs/introduction"),
("Reference", "/reference/"),
("Reference", "/hypermedia/reference/"),
("Protocols", "/protocols/wire-format"),
("Examples", "/examples/click-to-load"),
("Examples", "/hypermedia/examples/click-to-load"),
("Essays", "/essays/sx-sucks"),
]
@@ -744,7 +744,7 @@ ATTR_DETAILS: dict[str, dict] = {
),
"demo": "ref-get-demo",
"example": (
'(button :sx-get "/reference/api/time"\n'
'(button :sx-get "/hypermedia/reference/api/time"\n'
' :sx-target "#ref-get-result"\n'
' :sx-swap "innerHTML"\n'
' "Load server time")'
@@ -764,7 +764,7 @@ ATTR_DETAILS: dict[str, dict] = {
),
"demo": "ref-post-demo",
"example": (
'(form :sx-post "/reference/api/greet"\n'
'(form :sx-post "/hypermedia/reference/api/greet"\n'
' :sx-target "#ref-post-result"\n'
' :sx-swap "innerHTML"\n'
' (input :type "text" :name "name"\n'
@@ -786,7 +786,7 @@ ATTR_DETAILS: dict[str, dict] = {
),
"demo": "ref-put-demo",
"example": (
'(button :sx-put "/reference/api/status"\n'
'(button :sx-put "/hypermedia/reference/api/status"\n'
' :sx-target "#ref-put-view"\n'
' :sx-swap "innerHTML"\n'
' :sx-vals "{\\"status\\": \\"published\\"}"\n'
@@ -807,7 +807,7 @@ ATTR_DETAILS: dict[str, dict] = {
),
"demo": "ref-delete-demo",
"example": (
'(button :sx-delete "/reference/api/item/1"\n'
'(button :sx-delete "/hypermedia/reference/api/item/1"\n'
' :sx-target "#ref-del-1"\n'
' :sx-swap "delete"\n'
' "Remove")'
@@ -826,7 +826,7 @@ ATTR_DETAILS: dict[str, dict] = {
),
"demo": "ref-patch-demo",
"example": (
'(button :sx-patch "/reference/api/theme"\n'
'(button :sx-patch "/hypermedia/reference/api/theme"\n'
' :sx-vals "{\\"theme\\": \\"dark\\"}"\n'
' :sx-target "#ref-patch-val"\n'
' :sx-swap "innerHTML"\n'
@@ -852,7 +852,7 @@ ATTR_DETAILS: dict[str, dict] = {
"example": (
'(input :type "text" :name "q"\n'
' :placeholder "Type to search..."\n'
' :sx-get "/reference/api/trigger-search"\n'
' :sx-get "/hypermedia/reference/api/trigger-search"\n'
' :sx-trigger "input changed delay:300ms"\n'
' :sx-target "#ref-trigger-result"\n'
' :sx-swap "innerHTML")'
@@ -874,12 +874,12 @@ ATTR_DETAILS: dict[str, dict] = {
"demo": "ref-target-demo",
"example": (
';; Two buttons targeting different elements\n'
'(button :sx-get "/reference/api/time"\n'
'(button :sx-get "/hypermedia/reference/api/time"\n'
' :sx-target "#ref-target-a"\n'
' :sx-swap "innerHTML"\n'
' "Update Box A")\n'
'\n'
'(button :sx-get "/reference/api/time"\n'
'(button :sx-get "/hypermedia/reference/api/time"\n'
' :sx-target "#ref-target-b"\n'
' :sx-swap "innerHTML"\n'
' "Update Box B")'
@@ -894,13 +894,13 @@ ATTR_DETAILS: dict[str, dict] = {
"demo": "ref-swap-demo",
"example": (
';; Append to the end of a list\n'
'(button :sx-get "/reference/api/swap-item"\n'
'(button :sx-get "/hypermedia/reference/api/swap-item"\n'
' :sx-target "#ref-swap-list"\n'
' :sx-swap "beforeend"\n'
' "beforeend")\n'
'\n'
';; Prepend to the start\n'
'(button :sx-get "/reference/api/swap-item"\n'
'(button :sx-get "/hypermedia/reference/api/swap-item"\n'
' :sx-target "#ref-swap-list"\n'
' :sx-swap "afterbegin"\n'
' "afterbegin")'
@@ -921,7 +921,7 @@ ATTR_DETAILS: dict[str, dict] = {
),
"demo": "ref-oob-demo",
"example": (
'(button :sx-get "/reference/api/oob"\n'
'(button :sx-get "/hypermedia/reference/api/oob"\n'
' :sx-target "#ref-oob-main"\n'
' :sx-swap "innerHTML"\n'
' "Update both boxes")'
@@ -944,7 +944,7 @@ ATTR_DETAILS: dict[str, dict] = {
),
"demo": "ref-select-demo",
"example": (
'(button :sx-get "/reference/api/select-page"\n'
'(button :sx-get "/hypermedia/reference/api/select-page"\n'
' :sx-target "#ref-select-result"\n'
' :sx-select "#the-content"\n'
' :sx-swap "innerHTML"\n'
@@ -968,7 +968,7 @@ ATTR_DETAILS: dict[str, dict] = {
),
"demo": "ref-confirm-demo",
"example": (
'(button :sx-delete "/reference/api/item/confirm"\n'
'(button :sx-delete "/hypermedia/reference/api/item/confirm"\n'
' :sx-target "#ref-confirm-item"\n'
' :sx-swap "delete"\n'
' :sx-confirm "Are you sure you want to delete this file?"\n'
@@ -983,8 +983,8 @@ ATTR_DETAILS: dict[str, dict] = {
),
"demo": "ref-pushurl-demo",
"example": (
'(a :href "/reference/attributes/sx-get"\n'
' :sx-get "/reference/attributes/sx-get"\n'
'(a :href "/hypermedia/reference/attributes/sx-get"\n'
' :sx-get "/hypermedia/reference/attributes/sx-get"\n'
' :sx-target "#main-panel"\n'
' :sx-select "#main-panel"\n'
' :sx-swap "outerHTML"\n'
@@ -1003,7 +1003,7 @@ ATTR_DETAILS: dict[str, dict] = {
"example": (
'(input :type "text" :name "q"\n'
' :placeholder "Type quickly..."\n'
' :sx-get "/reference/api/slow-echo"\n'
' :sx-get "/hypermedia/reference/api/slow-echo"\n'
' :sx-trigger "input changed delay:100ms"\n'
' :sx-sync "replace"\n'
' :sx-target "#ref-sync-result"\n'
@@ -1024,7 +1024,7 @@ ATTR_DETAILS: dict[str, dict] = {
),
"demo": "ref-encoding-demo",
"example": (
'(form :sx-post "/reference/api/upload-name"\n'
'(form :sx-post "/hypermedia/reference/api/upload-name"\n'
' :sx-encoding "multipart/form-data"\n'
' :sx-target "#ref-encoding-result"\n'
' :sx-swap "innerHTML"\n'
@@ -1044,7 +1044,7 @@ ATTR_DETAILS: dict[str, dict] = {
),
"demo": "ref-headers-demo",
"example": (
'(button :sx-get "/reference/api/echo-headers"\n'
'(button :sx-get "/hypermedia/reference/api/echo-headers"\n'
' :sx-headers \'{"X-Custom-Token": "abc123", "X-Request-Source": "demo"}\'\n'
' :sx-target "#ref-headers-result"\n'
' :sx-swap "innerHTML"\n'
@@ -1073,7 +1073,7 @@ ATTR_DETAILS: dict[str, dict] = {
' (option :value "books" "Books")\n'
' (option :value "tools" "Tools"))\n'
'\n'
'(button :sx-get "/reference/api/echo-vals"\n'
'(button :sx-get "/hypermedia/reference/api/echo-vals"\n'
' :sx-include "#ref-inc-cat"\n'
' :sx-target "#ref-include-result"\n'
' :sx-swap "innerHTML"\n'
@@ -1097,7 +1097,7 @@ ATTR_DETAILS: dict[str, dict] = {
),
"demo": "ref-vals-demo",
"example": (
'(button :sx-post "/reference/api/echo-vals"\n'
'(button :sx-post "/hypermedia/reference/api/echo-vals"\n'
' :sx-vals \'{"source": "demo", "page": "3"}\'\n'
' :sx-target "#ref-vals-result"\n'
' :sx-swap "innerHTML"\n'
@@ -1112,8 +1112,8 @@ ATTR_DETAILS: dict[str, dict] = {
),
"demo": "ref-media-demo",
"example": (
'(a :href "/reference/attributes/sx-get"\n'
' :sx-get "/reference/attributes/sx-get"\n'
'(a :href "/hypermedia/reference/attributes/sx-get"\n'
' :sx-get "/hypermedia/reference/attributes/sx-get"\n'
' :sx-target "#main-panel"\n'
' :sx-select "#main-panel"\n'
' :sx-swap "outerHTML"\n'
@@ -1133,7 +1133,7 @@ ATTR_DETAILS: dict[str, dict] = {
';; Left box: sx works normally\n'
';; Right box: sx-disable prevents any sx behavior\n'
'(div :sx-disable "true"\n'
' (button :sx-get "/reference/api/time"\n'
' (button :sx-get "/hypermedia/reference/api/time"\n'
' :sx-target "#ref-dis-b"\n'
' :sx-swap "innerHTML"\n'
' "Load")\n'
@@ -1166,7 +1166,7 @@ ATTR_DETAILS: dict[str, dict] = {
),
"demo": "ref-retry-demo",
"example": (
'(button :sx-get "/reference/api/flaky"\n'
'(button :sx-get "/hypermedia/reference/api/flaky"\n'
' :sx-target "#ref-retry-result"\n'
' :sx-swap "innerHTML"\n'
' :sx-retry "true"\n'
@@ -1239,7 +1239,7 @@ ATTR_DETAILS: dict[str, dict] = {
),
"demo": "ref-preload-demo",
"example": (
'(button :sx-get "/reference/api/time"\n'
'(button :sx-get "/hypermedia/reference/api/time"\n'
' :sx-target "#ref-preload-result"\n'
' :sx-swap "innerHTML"\n'
' :sx-preload "mouseover"\n'
@@ -1275,7 +1275,7 @@ ATTR_DETAILS: dict[str, dict] = {
),
"demo": "ref-indicator-demo",
"example": (
'(button :sx-get "/reference/api/slow-echo"\n'
'(button :sx-get "/hypermedia/reference/api/slow-echo"\n'
' :sx-target "#ref-indicator-result"\n'
' :sx-swap "innerHTML"\n'
' :sx-indicator "#ref-spinner"\n'
@@ -1302,7 +1302,7 @@ ATTR_DETAILS: dict[str, dict] = {
),
"demo": "ref-validate-demo",
"example": (
'(form :sx-post "/reference/api/greet"\n'
'(form :sx-post "/hypermedia/reference/api/greet"\n'
' :sx-target "#ref-validate-result"\n'
' :sx-swap "innerHTML"\n'
' :sx-validate "true"\n'
@@ -1340,7 +1340,7 @@ ATTR_DETAILS: dict[str, dict] = {
),
"demo": "ref-optimistic-demo",
"example": (
'(button :sx-delete "/reference/api/item/opt1"\n'
'(button :sx-delete "/hypermedia/reference/api/item/opt1"\n'
' :sx-target "#ref-opt-item"\n'
' :sx-swap "delete"\n'
' :sx-optimistic "remove"\n'
@@ -1362,7 +1362,7 @@ ATTR_DETAILS: dict[str, dict] = {
),
"demo": "ref-replace-url-demo",
"example": (
'(button :sx-get "/reference/api/time"\n'
'(button :sx-get "/hypermedia/reference/api/time"\n'
' :sx-target "#ref-replurl-result"\n'
' :sx-swap "innerHTML"\n'
' :sx-replace-url "true"\n'
@@ -1378,7 +1378,7 @@ ATTR_DETAILS: dict[str, dict] = {
),
"demo": "ref-disabled-elt-demo",
"example": (
'(button :sx-get "/reference/api/slow-echo"\n'
'(button :sx-get "/hypermedia/reference/api/slow-echo"\n'
' :sx-target "#ref-diselt-result"\n'
' :sx-swap "innerHTML"\n'
' :sx-disabled-elt "this"\n'
@@ -1394,7 +1394,7 @@ ATTR_DETAILS: dict[str, dict] = {
),
"demo": "ref-prompt-demo",
"example": (
'(button :sx-get "/reference/api/prompt-echo"\n'
'(button :sx-get "/hypermedia/reference/api/prompt-echo"\n'
' :sx-target "#ref-prompt-result"\n'
' :sx-swap "innerHTML"\n'
' :sx-prompt "Enter your name:"\n'
@@ -1414,7 +1414,7 @@ ATTR_DETAILS: dict[str, dict] = {
),
"demo": "ref-params-demo",
"example": (
'(form :sx-post "/reference/api/echo-vals"\n'
'(form :sx-post "/hypermedia/reference/api/echo-vals"\n'
' :sx-target "#ref-params-result"\n'
' :sx-swap "innerHTML"\n'
' :sx-params "name"\n'
@@ -1433,7 +1433,7 @@ ATTR_DETAILS: dict[str, dict] = {
),
"demo": "ref-sse-demo",
"example": (
'(div :sx-sse "/reference/api/sse-time"\n'
'(div :sx-sse "/hypermedia/reference/api/sse-time"\n'
' :sx-sse-swap "time"\n'
' :sx-target "#ref-sse-result"\n'
' :sx-swap "innerHTML"\n'