feat: server-side echo suppression for compositor slider drags#175
Merged
streamer45 merged 4 commits intomainfrom Mar 19, 2026
Merged
feat: server-side echo suppression for compositor slider drags#175streamer45 merged 4 commits intomainfrom
streamer45 merged 4 commits intomainfrom
Conversation
Add TuneNodeSilent WebSocket action that broadcasts NodeParamsChanged to all clients EXCEPT the sender. This eliminates stale echo-backs during high-frequency slider drags (opacity, rotation) without blocking changes from other clients. Backend: - BroadcastEvent wrapper with optional exclude_conn_id in state.rs - Per-connection unique ID via ACTIVE_CONNECTIONS counter in websocket.rs - TuneNodeSilent variant in RequestPayload (crates/api/src/lib.rs) - handle_tune_node_silent handler that broadcasts with sender exclusion - All existing event_tx.send() calls wrapped with BroadcastEvent::to_all() Client: - tuneNodeConfigSilent in useSession.ts using 'tunenodesilent' action - onConfigChangeSilent threaded through MonitorView -> pipelineGraph -> CompositorNode -> useCompositorLayers -> compositorCommit - Throttled sends (slider drags) use silent adapter; non-throttled sends (drag-end, CRUD) use normal adapter - Removed isSliderActiveAtom and all client-side echo-back guards - Removed onSliderStart/onSliderEnd from OpacityControl/RotationControl Signed-off-by: StreamKit Devin <devin@streamkit.dev> Co-Authored-By: Claudio Costa <cstcld91@gmail.com>
Contributor
Author
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
ACTIVE_CONNECTIONS is decremented on disconnect, so reusing it as a connection ID source can cause ID collisions (e.g. A=0, B=1, A disconnects→counter=1, C connects→C=1 collides with B). Add a separate NEXT_CONN_ID AtomicU64 that is never decremented, ensuring connection IDs are globally unique across the server lifetime. Also add unit tests for BroadcastEvent exclusion filtering. Signed-off-by: StreamKit Devin <devin@streamkit.dev> Co-Authored-By: Claudio Costa <cstcld91@gmail.com>
…sends TuneNodeSilent suppresses NodeParamsChanged echo-back server-side, but NodeViewDataUpdated is still broadcast to all clients (the engine does not track the originating connection). During slider drags, stale view-data can overwrite the local atom's newer value. Add a throttleActiveRef in useCompositorCommit that is set to true when a throttled silent send fires and cleared via a trailing-edge debounce. Both sync guards (sync-from-props and useServerLayoutSync) now check this ref alongside dragStateRef, skipping updates while a throttled slider send is in-flight. Includes an integration test that verifies stale server view-data is correctly skipped during active throttled slider sends. Signed-off-by: StreamKit Devin <devin@streamkit.dev> Co-Authored-By: Claudio Costa <cstcld91@gmail.com>
Extract shared body of handle_tune_node_async and handle_tune_node_silent into handle_tune_node_fire_and_forget(exclude_conn_id: Option<u64>). Both thin wrappers now delegate to it with None / Some(conn_id). This eliminates ~100 lines of duplicated code (permission checks, session lookup, file security validation, pipeline model update, engine forwarding) that was a maintenance hazard — any future bug fix or security patch would have needed to be mirrored in both functions. Also simplifies redundant assertions in BroadcastEvent test and adds a note about the theoretical throttleActiveRef clear-timing edge case. Signed-off-by: StreamKit Devin <devin@streamkit.dev> Co-Authored-By: Claudio Costa <cstcld91@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements server-side echo suppression for compositor slider drags, eliminating the need for the client-side
isSliderActiveAtomguard.Backend (Rust):
BroadcastEventwrapper aroundApiEventwithexclude_conn_id: Option<u64>for per-connection filtering in the broadcast channelTuneNodeSilentWebSocket action — fire-and-forget likeTuneNodeAsync, but broadcastsNodeParamsChangedto all clients except the senderNEXT_CONN_ID) separate from theACTIVE_CONNECTIONSgauge to prevent ID reuse after disconnectsevent_tx.send()calls wrapped withBroadcastEvent::to_all()Client (TypeScript):
tuneNodeConfigSilenttouseSession— sendsTuneNodeSilentviasendFireAndForget()onConfigChangeSilentthroughMonitorView → pipelineGraph → CompositorNode → useCompositorLayers → compositorCommitisSliderActiveAtomand all associated guards,onSliderStart/onSliderEndprops from slider widgetsthrottleActiveRefinuseCompositorCommitto guard against staleNodeViewDataUpdatedecho-backs during slider drags (this event path is NOT suppressed byTuneNodeSilentsince it comes from the engine asynchronously)Two echo-back paths:
NodeParamsChanged— broadcast from handler → suppressed byTuneNodeSilentNodeViewDataUpdated— broadcast from engine view-data task → guarded client-side bythrottleActiveRef(set true during throttled sends, cleared via trailing-edge debounce)Review & Testing Checklist for Human
isSliderActiveAtomguard).tunenodecommit on drag-end broadcasts to all including sender).Notes
throttleActiveRefdebounce delay matchesthrottleMs(default 100ms fromPARAM_THROTTLE_MS), so the guard stays active for the full throttle window and clears shortly after the last tick.sessionId) is unaffected — it doesn't haveonConfigChangeSilent, sosilentCommitAdapteris null and throttled sends fall back to the normal adapter.Link to Devin session: https://staging.itsdev.in/sessions/f9f80a4c02884400a21cdda813991a11
Requested by: @streamer45