feat: core language & stream improvements
Language improvements: - Reactive container class: prop (wraps in DS.effect when signal-dependent) - Stream output filtering (_streamDiff skips non-output signals) - Stream exponential reconnect backoff (2s → 4s → 8s, max 30s) - Breakout game: classic row order with all 5 rows having collision Verified: reactive text + derived signals already work. All 48 examples compile, 136 tests pass
This commit is contained in:
parent
4ac584c81e
commit
d4c7ba2385
1 changed files with 28 additions and 3 deletions
|
|
@ -423,6 +423,18 @@ impl JsEmitter {
|
|||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Set output filter if stream has explicit output list
|
||||
if !stream.output.is_empty() {
|
||||
let names_js = stream.output.iter()
|
||||
.map(|n| format!("\"{}\"", n))
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
self.emit_line(&format!(
|
||||
"DS._streamOutputFilter = new Set([{}]);",
|
||||
names_js
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -564,7 +576,14 @@ impl JsEmitter {
|
|||
}
|
||||
"class" => {
|
||||
let js = self.emit_expr(val);
|
||||
self.emit_line(&format!("{}.className += ' ' + {};", node_var, js));
|
||||
if js.contains(".value") {
|
||||
self.emit_line(&format!(
|
||||
"DS.effect(() => {{ {}.className = 'ds-{}' + ' ' + {}; }});",
|
||||
node_var, container_tag, js
|
||||
));
|
||||
} else {
|
||||
self.emit_line(&format!("{}.className += ' ' + {};", node_var, js));
|
||||
}
|
||||
}
|
||||
"click" | "submit" => {
|
||||
let handler_js = self.emit_event_handler_expr(val);
|
||||
|
|
@ -2984,14 +3003,17 @@ const DS = (() => {
|
|||
_streamWs.onclose = function() {
|
||||
_streamConnected = false;
|
||||
_streamReconnects++;
|
||||
console.log('[ds-stream] Disconnected, reconnecting in 2s (attempt ' + _streamReconnects + ')');
|
||||
setTimeout(function() { _initStream(url, mode); }, 2000);
|
||||
var delay = Math.min(_reconnectDelay, 30000);
|
||||
console.log('[ds-stream] Disconnected, reconnecting in ' + (delay/1000) + 's (attempt ' + _streamReconnects + ')');
|
||||
setTimeout(function() { _initStream(url, mode); }, delay);
|
||||
_reconnectDelay = Math.min(_reconnectDelay * 2, 30000); // exponential backoff, max 30s
|
||||
};
|
||||
_streamWs.onerror = function() {
|
||||
_streamConnected = false;
|
||||
};
|
||||
_streamWs.onopen = function() {
|
||||
_streamConnected = true;
|
||||
_reconnectDelay = 2000; // reset backoff on success
|
||||
console.log('[ds-stream] Peer connected:', peerUrl);
|
||||
// Send schema announcement (0x32) with output signal list
|
||||
var outputNames = Object.keys(_signalRegistry);
|
||||
|
|
@ -3038,10 +3060,13 @@ const DS = (() => {
|
|||
// ── Batched diff: coalesce multiple signal changes into one WS frame ──
|
||||
var _lastSentValues = {}; // deduplication: track last-sent value per signal
|
||||
var _diffBatchCount = 0; // count batches for periodic auto-sync
|
||||
var _streamOutputFilter = null; // Set of signal names to stream (null = all)
|
||||
var _reconnectDelay = 2000; // exponential backoff: starts at 2s, max 30s
|
||||
|
||||
function _streamDiff(name, value) {
|
||||
if (!_streamWs || _streamWs.readyState !== 1 || _streamMode !== 'signal') return;
|
||||
if (_applyingRemoteDiff) return; // prevent echo loops
|
||||
if (_streamOutputFilter && !_streamOutputFilter.has(name)) return; // skip non-output signals
|
||||
var val = (typeof value === 'object' && value !== null && 'value' in value) ? value.value : value;
|
||||
// Deduplication: skip if value hasn't changed
|
||||
var lastStr = JSON.stringify(_lastSentValues[name]);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue