Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
41 lines
1.7 KiB
TypeScript
41 lines
1.7 KiB
TypeScript
import { useSyncExternalStoreWithSelector } from "use-sync-external-store/with-selector.js";
|
|
|
|
// Single React binding for workspace session engine subscriptions
|
|
// (docs/architecture/agent-gui-refactor-plan.md, sections 3.5 and 4.1).
|
|
//
|
|
// This is the only file in the package allowed to touch useSyncExternalStore
|
|
// (enforced by tools/scripts/check-agent-gui-degradation.mjs). Components
|
|
// subscribe through useEngineSelector so selector memoization and equality
|
|
// comparison are implemented exactly once, via React's official with-selector
|
|
// shim; hand-rolled subscriptions returning fresh references are the classic
|
|
// infinite re-render trap this file exists to contain.
|
|
|
|
/**
|
|
* Structural surface the binding needs from an engine instance. Matches
|
|
* AgentSessionEngine from @tutti-os/agent-activity-core, but stays structural
|
|
* so focused tests and future engine slices can bind without the full engine.
|
|
*
|
|
* Contract: `subscribe` and `getSnapshot` must be stable, this-free function
|
|
* references for the lifetime of the instance (the engine factory returns
|
|
* closures, satisfying this by construction). A fresh `subscribe` identity per
|
|
* render would force useSyncExternalStore to resubscribe on every render.
|
|
*/
|
|
export interface EngineStateStore<TState> {
|
|
getSnapshot(): TState;
|
|
subscribe(listener: () => void): () => void;
|
|
}
|
|
|
|
export function useEngineSelector<TState, TSelected>(
|
|
engine: EngineStateStore<TState>,
|
|
selector: (state: TState) => TSelected,
|
|
isEqual?: (a: TSelected, b: TSelected) => boolean
|
|
): TSelected {
|
|
return useSyncExternalStoreWithSelector(
|
|
engine.subscribe,
|
|
engine.getSnapshot,
|
|
engine.getSnapshot,
|
|
selector,
|
|
isEqual
|
|
);
|
|
}
|