Add CEK Machine section under Geography with live island demos
geography/cek.sx: overview page (three registers, deref-as-shift explanation) + demo page with 5 live islands (counter, computed chain, reactive attrs, stopwatch effect+cleanup, batch coalescing). Nav entry, router routes, defpage definitions. CEK exports (cekRun, makeCekState, makeReactiveResetFrame, evalExpr) added to Sx public API via platform_js.py. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
// =========================================================================
|
||||
|
||||
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
|
||||
var SX_VERSION = "2026-03-14T01:00:32Z";
|
||||
var SX_VERSION = "2026-03-14T01:23:35Z";
|
||||
|
||||
function isNil(x) { return x === NIL || x === null || x === undefined; }
|
||||
function isSxTruthy(x) { return x !== false && !isNil(x); }
|
||||
@@ -1391,7 +1391,7 @@ var d2 = hexDigitValue(nth(source, pos));
|
||||
var _ = (pos = (pos + 1));
|
||||
var d3 = hexDigitValue(nth(source, pos));
|
||||
var _ = (pos = (pos + 1));
|
||||
buf = (String(buf) + String(charFromCode(((((d0 * 4096) + (d1 * 256)) + (d2 * 16)) + d3))));
|
||||
buf = (String(buf) + String(charFromCode((d0 * 4096))));
|
||||
continue; } } else { buf = (String(buf) + String((isSxTruthy((esc == "n")) ? "\n" : (isSxTruthy((esc == "t")) ? "\t" : (isSxTruthy((esc == "r")) ? "\r" : esc)))));
|
||||
pos = (pos + 1);
|
||||
continue; } } } else { buf = (String(buf) + String(ch));
|
||||
@@ -6442,87 +6442,6 @@ return forEach(function(d) { return invoke(d); }, subDisposers); });
|
||||
};
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// Extension: Delimited continuations (shift/reset)
|
||||
// =========================================================================
|
||||
|
||||
function Continuation(fn) { this.fn = fn; }
|
||||
Continuation.prototype._continuation = true;
|
||||
Continuation.prototype.call = function(value) { return this.fn(value !== undefined ? value : NIL); };
|
||||
|
||||
function ShiftSignal(kName, body, env) {
|
||||
this.kName = kName;
|
||||
this.body = body;
|
||||
this.env = env;
|
||||
}
|
||||
|
||||
PRIMITIVES["continuation?"] = function(x) { return x != null && x._continuation === true; };
|
||||
|
||||
var _resetResume = [];
|
||||
|
||||
function sfReset(args, env) {
|
||||
var body = args[0];
|
||||
try {
|
||||
return trampoline(evalExpr(body, env));
|
||||
} catch (e) {
|
||||
if (e instanceof ShiftSignal) {
|
||||
var sig = e;
|
||||
var cont = new Continuation(function(value) {
|
||||
if (value === undefined) value = NIL;
|
||||
_resetResume.push(value);
|
||||
try {
|
||||
return trampoline(evalExpr(body, env));
|
||||
} finally {
|
||||
_resetResume.pop();
|
||||
}
|
||||
});
|
||||
var sigEnv = merge(sig.env);
|
||||
sigEnv[sig.kName] = cont;
|
||||
return trampoline(evalExpr(sig.body, sigEnv));
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
function sfShift(args, env) {
|
||||
if (_resetResume.length > 0) {
|
||||
return _resetResume[_resetResume.length - 1];
|
||||
}
|
||||
var kName = symbolName(args[0]);
|
||||
var body = args[1];
|
||||
throw new ShiftSignal(kName, body, env);
|
||||
}
|
||||
|
||||
// Wrap evalList to intercept reset/shift
|
||||
var _baseEvalList = evalList;
|
||||
evalList = function(expr, env) {
|
||||
var head = expr[0];
|
||||
if (isSym(head)) {
|
||||
var name = head.name;
|
||||
if (name === "reset") return sfReset(expr.slice(1), env);
|
||||
if (name === "shift") return sfShift(expr.slice(1), env);
|
||||
}
|
||||
return _baseEvalList(expr, env);
|
||||
};
|
||||
|
||||
// Wrap aserSpecial to handle reset/shift in SX wire mode
|
||||
if (typeof aserSpecial === "function") {
|
||||
var _baseAserSpecial = aserSpecial;
|
||||
aserSpecial = function(name, expr, env) {
|
||||
if (name === "reset") return sfReset(expr.slice(1), env);
|
||||
if (name === "shift") return sfShift(expr.slice(1), env);
|
||||
return _baseAserSpecial(name, expr, env);
|
||||
};
|
||||
}
|
||||
|
||||
// Wrap typeOf to recognize continuations
|
||||
var _baseTypeOf = typeOf;
|
||||
typeOf = function(x) {
|
||||
if (x != null && x._continuation) return "continuation";
|
||||
return _baseTypeOf(x);
|
||||
};
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// Async IO: Promise-aware rendering for client-side IO primitives
|
||||
// =========================================================================
|
||||
@@ -7320,6 +7239,14 @@ return forEach(function(d) { return invoke(d); }, subDisposers); });
|
||||
context: sxContext,
|
||||
emit: sxEmit,
|
||||
emitted: sxEmitted,
|
||||
cekRun: cekRun,
|
||||
makeCekState: makeCekState,
|
||||
makeCekValue: makeCekValue,
|
||||
cekStep: cekStep,
|
||||
cekTerminal: cekTerminal_p,
|
||||
cekValue: cekValue,
|
||||
makeReactiveResetFrame: makeReactiveResetFrame,
|
||||
evalExpr: evalExpr,
|
||||
_version: "ref-2.0 (boot+dom+engine+html+orchestration+parser+sx, bootstrap-compiled)"
|
||||
};
|
||||
|
||||
|
||||
@@ -3076,7 +3076,7 @@ def fixups_js(has_html, has_sx, has_dom, has_signals=False, has_deps=False, has_
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def public_api_js(has_html, has_sx, has_dom, has_engine, has_orch, has_boot, has_parser, adapter_label, has_deps=False, has_router=False, has_signals=False, has_page_helpers=False):
|
||||
def public_api_js(has_html, has_sx, has_dom, has_engine, has_orch, has_boot, has_parser, adapter_label, has_deps=False, has_router=False, has_signals=False, has_page_helpers=False, has_cek=False):
|
||||
# Parser: use compiled sxParse from parser.sx, or inline a minimal fallback
|
||||
if has_parser:
|
||||
parser = '''
|
||||
@@ -3272,6 +3272,15 @@ def public_api_js(has_html, has_sx, has_dom, has_engine, has_orch, has_boot, has
|
||||
api_lines.append(' context: sxContext,')
|
||||
api_lines.append(' emit: sxEmit,')
|
||||
api_lines.append(' emitted: sxEmitted,')
|
||||
if has_cek:
|
||||
api_lines.append(' cekRun: cekRun,')
|
||||
api_lines.append(' makeCekState: makeCekState,')
|
||||
api_lines.append(' makeCekValue: makeCekValue,')
|
||||
api_lines.append(' cekStep: cekStep,')
|
||||
api_lines.append(' cekTerminal: cekTerminal_p,')
|
||||
api_lines.append(' cekValue: cekValue,')
|
||||
api_lines.append(' makeReactiveResetFrame: makeReactiveResetFrame,')
|
||||
api_lines.append(' evalExpr: evalExpr,')
|
||||
api_lines.append(f' _version: "{version}"')
|
||||
api_lines.append(' };')
|
||||
api_lines.append('')
|
||||
|
||||
@@ -226,7 +226,7 @@ def compile_ref_to_js(
|
||||
parts.append(CONTINUATIONS_JS)
|
||||
if has_dom:
|
||||
parts.append(ASYNC_IO_JS)
|
||||
parts.append(public_api_js(has_html, has_sx, has_dom, has_engine, has_orch, has_boot, has_parser, adapter_label, has_deps, has_router, has_signals, has_page_helpers))
|
||||
parts.append(public_api_js(has_html, has_sx, has_dom, has_engine, has_orch, has_boot, has_parser, adapter_label, has_deps, has_router, has_signals, has_page_helpers, has_cek))
|
||||
parts.append(EPILOGUE)
|
||||
|
||||
build_ts = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
195
sx/sx/geography/cek.sx
Normal file
195
sx/sx/geography/cek.sx
Normal file
@@ -0,0 +1,195 @@
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; CEK Machine — Geography section
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Island demos
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
;; Counter: signal + deref in text position
|
||||
(defisland ~geography/cek/demo-counter (&key initial)
|
||||
(let ((count (signal (or initial 0)))
|
||||
(doubled (computed (fn () (* 2 (deref count))))))
|
||||
(div :class "rounded-lg border border-stone-200 p-4 space-y-2"
|
||||
(div :class "flex items-center gap-3"
|
||||
(button :class "px-3 py-1 rounded bg-violet-600 text-white text-sm"
|
||||
:on-click (fn (e) (swap! count dec)) "-")
|
||||
(span :class "text-2xl font-bold text-violet-700 min-w-[3ch] text-center"
|
||||
(deref count))
|
||||
(button :class "px-3 py-1 rounded bg-violet-600 text-white text-sm"
|
||||
:on-click (fn (e) (swap! count inc)) "+"))
|
||||
(p :class "text-sm text-stone-500"
|
||||
(str "doubled: " (deref doubled))))))
|
||||
|
||||
;; Computed chain: base -> doubled -> quadrupled
|
||||
(defisland ~geography/cek/demo-chain ()
|
||||
(let ((base (signal 1))
|
||||
(doubled (computed (fn () (* (deref base) 2))))
|
||||
(quadrupled (computed (fn () (* (deref doubled) 2)))))
|
||||
(div :class "rounded-lg border border-stone-200 p-4 space-y-2"
|
||||
(div :class "flex items-center gap-3"
|
||||
(button :class "px-3 py-1 rounded bg-violet-600 text-white text-sm"
|
||||
:on-click (fn (e) (swap! base dec)) "-")
|
||||
(span :class "text-2xl font-bold text-violet-700 min-w-[3ch] text-center"
|
||||
(deref base))
|
||||
(button :class "px-3 py-1 rounded bg-violet-600 text-white text-sm"
|
||||
:on-click (fn (e) (swap! base inc)) "+"))
|
||||
(p :class "text-sm text-stone-500"
|
||||
(str "doubled: " (deref doubled) " | quadrupled: " (deref quadrupled))))))
|
||||
|
||||
;; Reactive attribute: (deref sig) in :class position
|
||||
(defisland ~geography/cek/demo-reactive-attr ()
|
||||
(let ((danger (signal false)))
|
||||
(div :class "rounded-lg border border-stone-200 p-4 space-y-3"
|
||||
(button :class "px-3 py-1 rounded bg-violet-600 text-white text-sm"
|
||||
:on-click (fn (e) (swap! danger not))
|
||||
(if (deref danger) "Safe mode" "Danger mode"))
|
||||
(div :class (str "p-3 rounded font-medium transition-colors "
|
||||
(if (deref danger)
|
||||
"bg-red-100 text-red-800"
|
||||
"bg-green-100 text-green-800"))
|
||||
(if (deref danger)
|
||||
"DANGER: reactive class binding via CEK"
|
||||
"SAFE: reactive class binding via CEK")))))
|
||||
|
||||
;; Stopwatch: effect + cleanup
|
||||
(defisland ~geography/cek/demo-stopwatch ()
|
||||
(let ((running (signal false))
|
||||
(elapsed (signal 0))
|
||||
(time-text (create-text-node "0.0s"))
|
||||
(btn-text (create-text-node "Start")))
|
||||
(effect (fn ()
|
||||
(when (deref running)
|
||||
(let ((id (set-interval (fn () (swap! elapsed inc)) 100)))
|
||||
(fn () (clear-interval id))))))
|
||||
(effect (fn ()
|
||||
(let ((e (deref elapsed)))
|
||||
(dom-set-text-content time-text
|
||||
(str (floor (/ e 10)) "." (mod e 10) "s")))))
|
||||
(effect (fn ()
|
||||
(dom-set-text-content btn-text
|
||||
(if (deref running) "Stop" "Start"))))
|
||||
(div :class "rounded-lg border border-stone-200 p-4"
|
||||
(div :class "flex items-center gap-3"
|
||||
(span :class "text-2xl font-bold text-violet-700 font-mono min-w-[5ch]" time-text)
|
||||
(button :class "px-3 py-1 rounded bg-violet-600 text-white text-sm"
|
||||
:on-click (fn (e) (swap! running not)) btn-text)
|
||||
(button :class "px-3 py-1 rounded bg-stone-400 text-white text-sm"
|
||||
:on-click (fn (e) (reset! running false) (reset! elapsed 0)) "Reset")))))
|
||||
|
||||
;; Batch: two signals, one notification
|
||||
(defisland ~geography/cek/demo-batch ()
|
||||
(let ((first-sig (signal 0))
|
||||
(second-sig (signal 0))
|
||||
(renders (signal 0)))
|
||||
(effect (fn ()
|
||||
(deref first-sig) (deref second-sig)
|
||||
(swap! renders inc)))
|
||||
(div :class "rounded-lg border border-stone-200 p-4 space-y-2"
|
||||
(div :class "flex items-center gap-4 text-sm"
|
||||
(span (str "first: " (deref first-sig)))
|
||||
(span (str "second: " (deref second-sig)))
|
||||
(span :class "px-2 py-0.5 rounded bg-green-100 text-green-800 text-xs font-semibold"
|
||||
(str "renders: " (deref renders))))
|
||||
(div :class "flex items-center gap-2"
|
||||
(button :class "px-3 py-1 rounded bg-violet-600 text-white text-sm"
|
||||
:on-click (fn (e)
|
||||
(batch (fn ()
|
||||
(swap! first-sig inc)
|
||||
(swap! second-sig inc))))
|
||||
"Batch +1")
|
||||
(button :class "px-3 py-1 rounded bg-stone-400 text-white text-sm"
|
||||
:on-click (fn (e)
|
||||
(swap! first-sig inc)
|
||||
(swap! second-sig inc))
|
||||
"No-batch +1")))))
|
||||
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Overview page content
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~geography/cek/cek-content ()
|
||||
(~docs/page :title "CEK Machine"
|
||||
|
||||
(~docs/section :title "Three registers" :id "registers"
|
||||
(p "The CEK machine makes evaluation explicit. Every step is a pure function from state to state:")
|
||||
(ul :class "space-y-1 text-stone-600 list-disc pl-5"
|
||||
(li (strong "C") "ontrol — the expression being evaluated")
|
||||
(li (strong "E") "nvironment — the bindings in scope")
|
||||
(li (strong "K") "ontinuation — what to do with the result"))
|
||||
(p "The tree-walk evaluator uses the same three things, but hides them in the call stack. The CEK makes them " (em "data") " — inspectable, serializable, capturable."))
|
||||
|
||||
(~docs/section :title "Why it matters" :id "why"
|
||||
(p "Making the continuation explicit enables:")
|
||||
(ul :class "space-y-1 text-stone-600 list-disc pl-5"
|
||||
(li (strong "Stepping") " — pause evaluation, inspect state, resume")
|
||||
(li (strong "Serialization") " — save a computation mid-flight, restore later")
|
||||
(li (strong "Delimited continuations") " — " (code "shift") "/" (code "reset") " capture \"the rest of this expression\" as a value")
|
||||
(li (strong "Deref-as-shift") " — " (code "(deref sig)") " inside a reactive boundary captures the continuation as the subscriber")))
|
||||
|
||||
(~docs/section :title "Default evaluator" :id "default"
|
||||
(p "CEK is the default evaluator on both client (JS) and server (Python). Every " (code "eval-expr") " call goes through " (code "cek-run") ". The tree-walk evaluator is preserved as " (code "_tree_walk_eval_expr") " for test runners that interpret " (code ".sx") " files.")
|
||||
(p "The CEK is defined in two spec files:")
|
||||
(ul :class "space-y-1 text-stone-600 list-disc pl-5"
|
||||
(li (code "frames.sx") " — frame types (IfFrame, ArgFrame, ResetFrame, ReactiveResetFrame, ...)")
|
||||
(li (code "cek.sx") " — step function, run loop, special form handlers, continuation operations")))
|
||||
|
||||
(~docs/section :title "Deref as shift" :id "deref-as-shift"
|
||||
(p "The reactive payoff. When " (code "(deref sig)") " encounters a signal inside a " (code "reactive-reset") " boundary:")
|
||||
(ol :class "space-y-1 text-stone-600 list-decimal pl-5"
|
||||
(li (strong "Shift") " — capture all frames between here and the reactive-reset")
|
||||
(li (strong "Subscribe") " — register the captured continuation as a signal subscriber")
|
||||
(li (strong "Return") " — flow the current signal value through the rest of the expression"))
|
||||
(p "When the signal changes, the captured continuation is re-invoked with the new value. The " (code "update-fn") " on the ReactiveResetFrame mutates the DOM. No explicit " (code "effect()") " wrapping needed.")
|
||||
(~docs/code :code (highlight
|
||||
";; User writes:\n(div :class (str \"count-\" (deref counter))\n (str \"Value: \" (deref counter)))\n\n;; CEK sees (deref counter) → signal? → reactive-reset on stack?\n;; Yes: capture (str \"count-\" [HOLE]) as continuation\n;; Register as subscriber. Return current value.\n;; When counter changes: re-invoke continuation → update DOM."
|
||||
"lisp")))))
|
||||
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Demo page content
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~geography/cek/cek-demo-content ()
|
||||
(~docs/page :title "CEK Demo"
|
||||
|
||||
(~docs/section :title "What this demonstrates" :id "what"
|
||||
(p "These are " (strong "live islands") " evaluated by the CEK machine. Every " (code "eval-expr") " goes through " (code "cek-run") ". Every " (code "(deref sig)") " in an island creates a reactive DOM binding via continuation frames.")
|
||||
(p "The CEK machine is defined in " (code "cek.sx") " (160 lines) and " (code "frames.sx") " (100 lines) — pure s-expressions, bootstrapped to both JavaScript and Python."))
|
||||
|
||||
(~docs/section :title "1. Counter" :id "demo-counter"
|
||||
(p (code "(deref count)") " in text position creates a reactive text node. " (code "(deref doubled)") " is a computed that updates when count changes.")
|
||||
(~geography/cek/demo-counter :initial 0)
|
||||
(~docs/code :code (highlight
|
||||
"(defisland ~demo-counter (&key initial)\n (let ((count (signal (or initial 0)))\n (doubled (computed (fn () (* 2 (deref count))))))\n (div\n (button :on-click (fn (e) (swap! count dec)) \"-\")\n (span (deref count))\n (button :on-click (fn (e) (swap! count inc)) \"+\")\n (p (str \"doubled: \" (deref doubled))))))"
|
||||
"lisp")))
|
||||
|
||||
(~docs/section :title "2. Computed chain" :id "demo-chain"
|
||||
(p "Three levels of computed: base -> doubled -> quadrupled. Change base, all propagate.")
|
||||
(~geography/cek/demo-chain)
|
||||
(~docs/code :code (highlight
|
||||
"(let ((base (signal 1))\n (doubled (computed (fn () (* (deref base) 2))))\n (quadrupled (computed (fn () (* (deref doubled) 2)))))\n (span (deref base))\n (p (str \"doubled: \" (deref doubled)\n \" | quadrupled: \" (deref quadrupled))))"
|
||||
"lisp")))
|
||||
|
||||
(~docs/section :title "3. Reactive attributes" :id "demo-attr"
|
||||
(p (code "(deref sig)") " in " (code ":class") " position. The CEK evaluates the " (code "str") " expression, and when the signal changes, the continuation re-evaluates and updates the attribute.")
|
||||
(~geography/cek/demo-reactive-attr)
|
||||
(~docs/code :code (highlight
|
||||
"(div :class (str \"p-3 rounded font-medium \"\n (if (deref danger)\n \"bg-red-100 text-red-800\"\n \"bg-green-100 text-green-800\"))\n (if (deref danger) \"DANGER\" \"SAFE\"))"
|
||||
"lisp")))
|
||||
|
||||
(~docs/section :title "4. Effect + cleanup" :id "demo-stopwatch"
|
||||
(p "Effects still work through CEK. This stopwatch uses " (code "effect") " with cleanup — toggling the signal clears the interval.")
|
||||
(~geography/cek/demo-stopwatch)
|
||||
(~docs/code :code (highlight
|
||||
"(effect (fn ()\n (when (deref running)\n (let ((id (set-interval (fn () (swap! elapsed inc)) 100)))\n (fn () (clear-interval id))))))"
|
||||
"lisp")))
|
||||
|
||||
(~docs/section :title "5. Batch coalescing" :id "demo-batch"
|
||||
(p "Two signals updated in " (code "batch") " — one notification cycle. Compare render counts between batch and no-batch.")
|
||||
(~geography/cek/demo-batch)
|
||||
(~docs/code :code (highlight
|
||||
"(batch (fn ()\n (swap! first-sig inc)\n (swap! second-sig inc)))\n;; One render pass, not two."
|
||||
"lisp")))))
|
||||
@@ -166,6 +166,12 @@
|
||||
(dict :label "Optimistic" :href "/sx/(geography.(isomorphism.optimistic))")
|
||||
(dict :label "Offline" :href "/sx/(geography.(isomorphism.offline))")))
|
||||
|
||||
(define cek-nav-items (list
|
||||
(dict :label "Overview" :href "/sx/(geography.(cek))"
|
||||
:summary "The CEK machine — explicit evaluator with Control, Environment, Kontinuation. Three registers, pure step function.")
|
||||
(dict :label "Demo" :href "/sx/(geography.(cek.demo))"
|
||||
:summary "Live islands evaluated by the CEK machine. Counter, computed chains, reactive attributes — all through explicit continuation frames.")))
|
||||
|
||||
(define plans-nav-items (list
|
||||
(dict :label "Status" :href "/sx/(etc.(plan.status))"
|
||||
:summary "Audit of all plans — what's done, what's in progress, and what remains.")
|
||||
@@ -384,7 +390,8 @@
|
||||
:summary "Child-to-parent communication across render boundaries — spread, collect!, reactive-spread, built on scopes."}
|
||||
{: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."}
|
||||
{:label "Isomorphism" :href "/sx/(geography.(isomorphism))" :children isomorphism-nav-items})}
|
||||
{:label "Isomorphism" :href "/sx/(geography.(isomorphism))" :children isomorphism-nav-items}
|
||||
{:label "CEK Machine" :href "/sx/(geography.(cek))" :children cek-nav-items})}
|
||||
{:label "Language" :href "/sx/(language)"
|
||||
:children (list
|
||||
{:label "Docs" :href "/sx/(language.(doc))" :children docs-nav-items}
|
||||
|
||||
@@ -650,6 +650,25 @@
|
||||
:layout :sx-docs
|
||||
:content (~layouts/doc :path "/sx/(geography.(marshes))" (~reactive-islands/marshes/reactive-islands-marshes-content)))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; CEK Machine section (under Geography)
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defpage cek-index
|
||||
:path "/geography/cek/"
|
||||
:auth :public
|
||||
:layout :sx-docs
|
||||
:content (~layouts/doc :path "/sx/(geography.(cek))" (~geography/cek/cek-content)))
|
||||
|
||||
(defpage cek-page
|
||||
:path "/geography/cek/<slug>"
|
||||
:auth :public
|
||||
:layout :sx-docs
|
||||
:content (~layouts/doc :path (str "/sx/(geography.(cek." slug "))")
|
||||
(case slug
|
||||
"demo" (~geography/cek/cek-demo-content)
|
||||
:else (~geography/cek/cek-content))))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Bootstrapped page helpers demo
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
@@ -267,6 +267,8 @@ _REDIRECT_PATTERNS = [
|
||||
lambda m: f"/sx/(geography.(reactive.{m.group(1)}))"),
|
||||
(re.compile(r"^/geography/isomorphism/(.+?)/?$"),
|
||||
lambda m: f"/sx/(geography.(isomorphism.{m.group(1)}))"),
|
||||
(re.compile(r"^/geography/cek/(.+?)/?$"),
|
||||
lambda m: f"/sx/(geography.(cek.{m.group(1)}))"),
|
||||
(re.compile(r"^/geography/spreads/?$"),
|
||||
"/sx/(geography.(spreads))"),
|
||||
(re.compile(r"^/geography/marshes/?$"),
|
||||
@@ -290,6 +292,7 @@ _REDIRECT_PATTERNS = [
|
||||
(re.compile(r"^/geography/hypermedia/?$"), "/sx/(geography.(hypermedia))"),
|
||||
(re.compile(r"^/geography/reactive/?$"), "/sx/(geography.(reactive))"),
|
||||
(re.compile(r"^/geography/isomorphism/?$"), "/sx/(geography.(isomorphism))"),
|
||||
(re.compile(r"^/geography/cek/?$"), "/sx/(geography.(cek))"),
|
||||
(re.compile(r"^/geography/?$"), "/sx/(geography)"),
|
||||
(re.compile(r"^/applications/cssx/?$"), "/sx/(applications.(cssx))"),
|
||||
(re.compile(r"^/applications/protocols/?$"), "/sx/(applications.(protocol))"),
|
||||
|
||||
Reference in New Issue
Block a user