Coalesce session presentation invalidations through a disposable frame queue

This commit is contained in:
2026-11-09 17:41:02 +08:00
parent 075bb79443
commit 04a9b0c2b2
6 changed files with 53 additions and 1 deletions
+16
View File
@@ -0,0 +1,16 @@
import type {SessionSnapshot} from '../canvas-core/gestures.ts';
import type {ExternalSource} from './selectors.ts';
import {createFrameQueue,type FrameClock} from './frame-queue.ts';
/** One subscription owns one disposable frame queue. Reads always see current sessions. */
export function createSessionFrameSource(source:ExternalSource<SessionSnapshot>,clock:FrameClock):ExternalSource<SessionSnapshot>{
return Object.freeze({getSnapshot:source.getSnapshot,subscribe(notify:()=>void){
let previous=source.getSnapshot();const queue=createFrameQueue<string>(clock,()=>notify());
const stop=source.subscribe(()=>{const next=source.getSnapshot(),dirty=new Set<string>();
for(const [id,view]of previous.views)if(next.views.get(id)!==view)dirty.add(`view:${id}`);
for(const [id,view]of next.views)if(previous.views.get(id)!==view)dirty.add(`view:${id}`);
if(previous.gesture!==next.gesture)for(const state of [previous.gesture,next.gesture])for(const target of state?.targets??[])dirty.add(`element:${target.id}`);
if(previous.connectionGesture!==next.connectionGesture)for(const state of [previous.connectionGesture,next.connectionGesture])if(state)dirty.add(`element:${state.target.id}`);
previous=next;queue.invalidate(dirty);
});return()=>{stop();queue.dispose();};
}});
}