源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
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
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
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
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
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user