Add spread + collect primitives, rewrite ~cssx/tw as defcomp
New SX primitives for child-to-parent communication in the render tree: - spread type: make-spread, spread?, spread-attrs — child injects attrs onto parent element (class joins with space, style with semicolon) - collect!/collected/clear-collected! — render-time accumulation with dedup into named buckets ~cssx/tw is now a proper defcomp returning a spread value instead of a macro wrapping children. ~cssx/flush reads collected "cssx" rules and emits a single <style data-cssx> tag. All four render adapters (html, async, dom, aser) handle spread values. Both bootstraps (Python + JS) regenerated. Also fixes length→len in cssx.sx (length was never a registered primitive). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -864,6 +864,11 @@ PREAMBLE = '''\
|
||||
function RawHTML(html) { this.html = html; }
|
||||
RawHTML.prototype._raw = true;
|
||||
|
||||
function SxSpread(attrs) { this.attrs = attrs || {}; }
|
||||
SxSpread.prototype._spread = true;
|
||||
|
||||
var _collectBuckets = {};
|
||||
|
||||
function isSym(x) { return x != null && x._sym === true; }
|
||||
function isKw(x) { return x != null && x._kw === true; }
|
||||
|
||||
@@ -1073,6 +1078,16 @@ PRIMITIVES_JS_MODULES: dict[str, str] = {
|
||||
return true;
|
||||
};
|
||||
''',
|
||||
|
||||
"stdlib.spread": '''
|
||||
// stdlib.spread — spread + collect primitives
|
||||
PRIMITIVES["make-spread"] = makeSpread;
|
||||
PRIMITIVES["spread?"] = isSpread;
|
||||
PRIMITIVES["spread-attrs"] = spreadAttrs;
|
||||
PRIMITIVES["collect!"] = sxCollect;
|
||||
PRIMITIVES["collected"] = sxCollected;
|
||||
PRIMITIVES["clear-collected!"] = sxClearCollected;
|
||||
''',
|
||||
}
|
||||
# Modules to include by default (all)
|
||||
_ALL_JS_MODULES = list(PRIMITIVES_JS_MODULES.keys())
|
||||
@@ -1108,6 +1123,7 @@ PLATFORM_JS_PRE = '''
|
||||
if (x._component) return "component";
|
||||
if (x._island) return "island";
|
||||
if (x._signal) return "signal";
|
||||
if (x._spread) return "spread";
|
||||
if (x._macro) return "macro";
|
||||
if (x._raw) return "raw-html";
|
||||
if (typeof Node !== "undefined" && x instanceof Node) return "dom-node";
|
||||
@@ -1130,6 +1146,22 @@ PLATFORM_JS_PRE = '''
|
||||
}
|
||||
function makeThunk(expr, env) { return new Thunk(expr, env); }
|
||||
|
||||
function makeSpread(attrs) { return new SxSpread(attrs || {}); }
|
||||
function isSpread(x) { return x != null && x._spread === true; }
|
||||
function spreadAttrs(s) { return s && s._spread ? s.attrs : {}; }
|
||||
|
||||
function sxCollect(bucket, value) {
|
||||
if (!_collectBuckets[bucket]) _collectBuckets[bucket] = [];
|
||||
var items = _collectBuckets[bucket];
|
||||
if (items.indexOf(value) === -1) items.push(value);
|
||||
}
|
||||
function sxCollected(bucket) {
|
||||
return _collectBuckets[bucket] ? _collectBuckets[bucket].slice() : [];
|
||||
}
|
||||
function sxClearCollected(bucket) {
|
||||
if (_collectBuckets[bucket]) _collectBuckets[bucket] = [];
|
||||
}
|
||||
|
||||
function lambdaParams(f) { return f.params; }
|
||||
function lambdaBody(f) { return f.body; }
|
||||
function lambdaClosure(f) { return f.closure; }
|
||||
@@ -3154,6 +3186,12 @@ def public_api_js(has_html, has_sx, has_dom, has_engine, has_orch, has_boot, has
|
||||
api_lines.append(' emitEvent: emitEvent,')
|
||||
api_lines.append(' onEvent: onEvent,')
|
||||
api_lines.append(' bridgeEvent: bridgeEvent,')
|
||||
api_lines.append(' makeSpread: makeSpread,')
|
||||
api_lines.append(' isSpread: isSpread,')
|
||||
api_lines.append(' spreadAttrs: spreadAttrs,')
|
||||
api_lines.append(' collect: sxCollect,')
|
||||
api_lines.append(' collected: sxCollected,')
|
||||
api_lines.append(' clearCollected: sxClearCollected,')
|
||||
api_lines.append(f' _version: "{version}"')
|
||||
api_lines.append(' };')
|
||||
api_lines.append('')
|
||||
|
||||
Reference in New Issue
Block a user