Try client-side routing for all sx-get link clicks, not just boost links

bind-event now checks tryClientRoute before executeRequest for GET
clicks on links. Previously only boost links (inside [sx-boost]
containers) attempted client routing — explicit sx-get links like
~nav-link always hit the network. Now essay/doc nav links render
client-side when possible.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 20:31:23 +00:00
parent 78c3ff30dd
commit f70861c175
2 changed files with 19 additions and 292 deletions

View File

@@ -512,92 +512,6 @@
return NIL;
}
// =========================================================================
// Platform: deps module — component dependency analysis
// =========================================================================
function componentDeps(c) {
return c.deps ? c.deps.slice() : [];
}
function componentSetDeps(c, deps) {
c.deps = deps;
}
function componentCssClasses(c) {
return c.cssClasses ? c.cssClasses.slice() : [];
}
function envComponents(env) {
var names = [];
for (var k in env) {
var v = env[k];
if (v && (v._component || v._macro)) names.push(k);
}
return names;
}
function regexFindAll(pattern, source) {
var re = new RegExp(pattern, "g");
var results = [];
var m;
while ((m = re.exec(source)) !== null) {
if (m[1] !== undefined) results.push(m[1]);
else results.push(m[0]);
}
return results;
}
function scanCssClasses(source) {
var classes = {};
var result = [];
var m;
var re1 = /:class\s+"([^"]*)"/g;
while ((m = re1.exec(source)) !== null) {
var parts = m[1].split(/\s+/);
for (var i = 0; i < parts.length; i++) {
if (parts[i] && !classes[parts[i]]) {
classes[parts[i]] = true;
result.push(parts[i]);
}
}
}
var re2 = /:class\s+\(str\s+((?:"[^"]*"\s*)+)\)/g;
while ((m = re2.exec(source)) !== null) {
var re3 = /"([^"]*)"/g;
var m2;
while ((m2 = re3.exec(m[1])) !== null) {
var parts2 = m2[1].split(/\s+/);
for (var j = 0; j < parts2.length; j++) {
if (parts2[j] && !classes[parts2[j]]) {
classes[parts2[j]] = true;
result.push(parts2[j]);
}
}
}
}
var re4 = /;;\s*@css\s+(.+)/g;
while ((m = re4.exec(source)) !== null) {
var parts3 = m[1].split(/\s+/);
for (var k = 0; k < parts3.length; k++) {
if (parts3[k] && !classes[parts3[k]]) {
classes[parts3[k]] = true;
result.push(parts3[k]);
}
}
}
return result;
}
function componentIoRefs(c) {
return c.ioRefs ? c.ioRefs.slice() : [];
}
function componentSetIoRefs(c, refs) {
c.ioRefs = refs;
}
// =========================================================================
// Platform interface — Parser
// =========================================================================
@@ -1935,7 +1849,7 @@ return postSwap(target); });
return (isSxTruthy((val == lastVal)) ? (shouldFire = false) : (lastVal = val));
})();
}
return (isSxTruthy(shouldFire) ? ((isSxTruthy(sxOr((eventName == "submit"), (isSxTruthy((eventName == "click")) && domHasAttr(el, "href")))) ? preventDefault_(e) : NIL), (isSxTruthy(get(mods, "delay")) ? (clearTimeout_(timer), (timer = setTimeout_(function() { return executeRequest(el, verbInfo, NIL); }, get(mods, "delay")))) : executeRequest(el, verbInfo, NIL))) : NIL);
return (isSxTruthy(shouldFire) ? ((isSxTruthy(sxOr((eventName == "submit"), (isSxTruthy((eventName == "click")) && domHasAttr(el, "href")))) ? preventDefault_(e) : NIL), (isSxTruthy((isSxTruthy((eventName == "click")) && isSxTruthy((get(verbInfo, "method") == "GET")) && isSxTruthy(domHasAttr(el, "href")) && isSxTruthy(!get(mods, "delay")) && tryClientRoute(urlPathname(get(verbInfo, "url"))))) ? (browserPushState(get(verbInfo, "url")), browserScrollTo(0, 0)) : (isSxTruthy(get(mods, "delay")) ? (clearTimeout_(timer), (timer = setTimeout_(function() { return executeRequest(el, verbInfo, NIL); }, get(mods, "delay")))) : executeRequest(el, verbInfo, NIL)))) : NIL);
})(); }, (isSxTruthy(get(mods, "once")) ? {["once"]: true} : NIL)) : NIL);
})(); };
@@ -2438,189 +2352,6 @@ callExpr.push(dictGet(kwargs, k)); } }
var bootInit = function() { return (initCssTracking(), initStyleDict(), processSxScripts(NIL), processPageScripts(), sxHydrateElements(NIL), processElements(NIL)); };
// === Transpiled from deps (component dependency analysis) ===
// scan-refs
var scanRefs = function(node) { return (function() {
var refs = [];
scanRefsWalk(node, refs);
return refs;
})(); };
// scan-refs-walk
var scanRefsWalk = function(node, refs) { return (isSxTruthy((typeOf(node) == "symbol")) ? (function() {
var name = symbolName(node);
return (isSxTruthy(startsWith(name, "~")) ? (isSxTruthy(!contains(refs, name)) ? append_b(refs, name) : NIL) : NIL);
})() : (isSxTruthy((typeOf(node) == "list")) ? forEach(function(item) { return scanRefsWalk(item, refs); }, node) : (isSxTruthy((typeOf(node) == "dict")) ? forEach(function(key) { return scanRefsWalk(dictGet(node, key), refs); }, keys(node)) : NIL))); };
// transitive-deps-walk
var transitiveDepsWalk = function(n, seen, env) { return (isSxTruthy(!contains(seen, n)) ? (append_b(seen, n), (function() {
var val = envGet(env, n);
return (isSxTruthy((typeOf(val) == "component")) ? forEach(function(ref) { return transitiveDepsWalk(ref, seen, env); }, scanRefs(componentBody(val))) : (isSxTruthy((typeOf(val) == "macro")) ? forEach(function(ref) { return transitiveDepsWalk(ref, seen, env); }, scanRefs(macroBody(val))) : NIL));
})()) : NIL); };
// transitive-deps
var transitiveDeps = function(name, env) { return (function() {
var seen = [];
var key = (isSxTruthy(startsWith(name, "~")) ? name : (String("~") + String(name)));
transitiveDepsWalk(key, seen, env);
return filter(function(x) { return !(x == key); }, seen);
})(); };
// compute-all-deps
var computeAllDeps = function(env) { return forEach(function(name) { return (function() {
var val = envGet(env, name);
return (isSxTruthy((typeOf(val) == "component")) ? componentSetDeps(val, transitiveDeps(name, env)) : NIL);
})(); }, envComponents(env)); };
// scan-components-from-source
var scanComponentsFromSource = function(source) { return (function() {
var matches = regexFindAll("\\(~([a-zA-Z_][a-zA-Z0-9_\\-]*)", source);
return map(function(m) { return (String("~") + String(m)); }, matches);
})(); };
// components-needed
var componentsNeeded = function(pageSource, env) { return (function() {
var direct = scanComponentsFromSource(pageSource);
var allNeeded = [];
{ var _c = direct; for (var _i = 0; _i < _c.length; _i++) { var name = _c[_i]; if (isSxTruthy(!contains(allNeeded, name))) {
allNeeded.push(name);
}
(function() {
var val = envGet(env, name);
return (function() {
var deps = (isSxTruthy((isSxTruthy((typeOf(val) == "component")) && !isEmpty(componentDeps(val)))) ? componentDeps(val) : transitiveDeps(name, env));
return forEach(function(dep) { return (isSxTruthy(!contains(allNeeded, dep)) ? append_b(allNeeded, dep) : NIL); }, deps);
})();
})(); } }
return allNeeded;
})(); };
// page-component-bundle
var pageComponentBundle = function(pageSource, env) { return componentsNeeded(pageSource, env); };
// page-css-classes
var pageCssClasses = function(pageSource, env) { return (function() {
var needed = componentsNeeded(pageSource, env);
var classes = [];
{ var _c = needed; for (var _i = 0; _i < _c.length; _i++) { var name = _c[_i]; (function() {
var val = envGet(env, name);
return (isSxTruthy((typeOf(val) == "component")) ? forEach(function(cls) { return (isSxTruthy(!contains(classes, cls)) ? append_b(classes, cls) : NIL); }, componentCssClasses(val)) : NIL);
})(); } }
{ var _c = scanCssClasses(pageSource); for (var _i = 0; _i < _c.length; _i++) { var cls = _c[_i]; if (isSxTruthy(!contains(classes, cls))) {
classes.push(cls);
} } }
return classes;
})(); };
// scan-io-refs-walk
var scanIoRefsWalk = function(node, ioNames, refs) { return (isSxTruthy((typeOf(node) == "symbol")) ? (function() {
var name = symbolName(node);
return (isSxTruthy(contains(ioNames, name)) ? (isSxTruthy(!contains(refs, name)) ? append_b(refs, name) : NIL) : NIL);
})() : (isSxTruthy((typeOf(node) == "list")) ? forEach(function(item) { return scanIoRefsWalk(item, ioNames, refs); }, node) : (isSxTruthy((typeOf(node) == "dict")) ? forEach(function(key) { return scanIoRefsWalk(dictGet(node, key), ioNames, refs); }, keys(node)) : NIL))); };
// scan-io-refs
var scanIoRefs = function(node, ioNames) { return (function() {
var refs = [];
scanIoRefsWalk(node, ioNames, refs);
return refs;
})(); };
// transitive-io-refs-walk
var transitiveIoRefsWalk = function(n, seen, allRefs, env, ioNames) { return (isSxTruthy(!contains(seen, n)) ? (append_b(seen, n), (function() {
var val = envGet(env, n);
return (isSxTruthy((typeOf(val) == "component")) ? (forEach(function(ref) { return (isSxTruthy(!contains(allRefs, ref)) ? append_b(allRefs, ref) : NIL); }, scanIoRefs(componentBody(val), ioNames)), forEach(function(dep) { return transitiveIoRefsWalk(dep, seen, allRefs, env, ioNames); }, scanRefs(componentBody(val)))) : (isSxTruthy((typeOf(val) == "macro")) ? (forEach(function(ref) { return (isSxTruthy(!contains(allRefs, ref)) ? append_b(allRefs, ref) : NIL); }, scanIoRefs(macroBody(val), ioNames)), forEach(function(dep) { return transitiveIoRefsWalk(dep, seen, allRefs, env, ioNames); }, scanRefs(macroBody(val)))) : NIL));
})()) : NIL); };
// transitive-io-refs
var transitiveIoRefs = function(name, env, ioNames) { return (function() {
var allRefs = [];
var seen = [];
var key = (isSxTruthy(startsWith(name, "~")) ? name : (String("~") + String(name)));
transitiveIoRefsWalk(key, seen, allRefs, env, ioNames);
return allRefs;
})(); };
// compute-all-io-refs
var computeAllIoRefs = function(env, ioNames) { return forEach(function(name) { return (function() {
var val = envGet(env, name);
return (isSxTruthy((typeOf(val) == "component")) ? componentSetIoRefs(val, transitiveIoRefs(name, env, ioNames)) : NIL);
})(); }, envComponents(env)); };
// component-pure?
var componentPure_p = function(name, env, ioNames) { return isEmpty(transitiveIoRefs(name, env, ioNames)); };
// === Transpiled from router (client-side route matching) ===
// split-path-segments
var splitPathSegments = function(path) { return (function() {
var trimmed = (isSxTruthy(startsWith(path, "/")) ? slice(path, 1) : path);
return (function() {
var trimmed2 = (isSxTruthy((isSxTruthy(!isEmpty(trimmed)) && endsWith(trimmed, "/"))) ? slice(trimmed, 0, (length(trimmed) - 1)) : trimmed);
return (isSxTruthy(isEmpty(trimmed2)) ? [] : split(trimmed2, "/"));
})();
})(); };
// make-route-segment
var makeRouteSegment = function(seg) { return (isSxTruthy((isSxTruthy(startsWith(seg, "<")) && endsWith(seg, ">"))) ? (function() {
var paramName = slice(seg, 1, (length(seg) - 1));
return (function() {
var d = {};
d["type"] = "param";
d["value"] = paramName;
return d;
})();
})() : (function() {
var d = {};
d["type"] = "literal";
d["value"] = seg;
return d;
})()); };
// parse-route-pattern
var parseRoutePattern = function(pattern) { return (function() {
var segments = splitPathSegments(pattern);
return map(makeRouteSegment, segments);
})(); };
// match-route-segments
var matchRouteSegments = function(pathSegs, parsedSegs) { return (isSxTruthy(!(length(pathSegs) == length(parsedSegs))) ? NIL : (function() {
var params = {};
var matched = true;
forEachIndexed(function(i, parsedSeg) { return (isSxTruthy(matched) ? (function() {
var pathSeg = nth(pathSegs, i);
var segType = get(parsedSeg, "type");
return (isSxTruthy((segType == "literal")) ? (isSxTruthy(!(pathSeg == get(parsedSeg, "value"))) ? (matched = false) : NIL) : (isSxTruthy((segType == "param")) ? dictSet(params, get(parsedSeg, "value"), pathSeg) : (matched = false)));
})() : NIL); }, parsedSegs);
return (isSxTruthy(matched) ? params : NIL);
})()); };
// match-route
var matchRoute = function(path, pattern) { return (function() {
var pathSegs = splitPathSegments(path);
var parsedSegs = parseRoutePattern(pattern);
return matchRouteSegments(pathSegs, parsedSegs);
})(); };
// find-matching-route
var findMatchingRoute = function(path, routes) { return (function() {
var pathSegs = splitPathSegments(path);
var result = NIL;
{ var _c = routes; for (var _i = 0; _i < _c.length; _i++) { var route = _c[_i]; if (isSxTruthy(isNil(result))) {
(function() {
var params = matchRouteSegments(pathSegs, get(route, "parsed"));
return (isSxTruthy(!isNil(params)) ? (function() {
var matched = merge(route, {});
matched["params"] = params;
return (result = matched);
})() : NIL);
})();
} } }
return result;
})(); };
// =========================================================================
// Platform interface — DOM adapter (browser-only)
// =========================================================================
@@ -3753,20 +3484,6 @@ callExpr.push(dictGet(kwargs, k)); } }
renderComponent: typeof sxRenderComponent === "function" ? sxRenderComponent : null,
getEnv: function() { return componentEnv; },
init: typeof bootInit === "function" ? bootInit : null,
scanRefs: scanRefs,
transitiveDeps: transitiveDeps,
computeAllDeps: computeAllDeps,
componentsNeeded: componentsNeeded,
pageComponentBundle: pageComponentBundle,
pageCssClasses: pageCssClasses,
scanIoRefs: scanIoRefs,
transitiveIoRefs: transitiveIoRefs,
computeAllIoRefs: computeAllIoRefs,
componentPure_p: componentPure_p,
splitPathSegments: splitPathSegments,
parseRoutePattern: parseRoutePattern,
matchRoute: matchRoute,
findMatchingRoute: findMatchingRoute,
_version: "ref-2.0 (boot+cssx+dom+engine+html+orchestration+parser+sx, bootstrap-compiled)"
};