2026-07-19 21:17:57 +08:00
|
|
|
/**
|
2026-07-23 02:56:16 +08:00
|
|
|
* uSES bridge: turns any bare observable snapshot source into a typed
|
|
|
|
|
* selector hook. Client-side-rendered only, so no server snapshot is wired.
|
|
|
|
|
* This is the ONE hook constructor in the client stack — engines and hosts
|
|
|
|
|
* traffic in bare sources; binding happens on the React side.
|
2026-07-19 21:17:57 +08:00
|
|
|
*/
|
|
|
|
|
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector.js'
|
2026-07-23 02:56:16 +08:00
|
|
|
import type { HostObservable, SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots'
|
2026-07-19 21:17:57 +08:00
|
|
|
|
|
|
|
|
/**
|
2026-07-23 02:56:16 +08:00
|
|
|
* Bind a bare observable source to a typed uSES selector hook.
|
2026-07-19 21:17:57 +08:00
|
|
|
* subscribe/getSnapshot are captured once per source into stable closures
|
|
|
|
|
* (also re-binds `this` for method-based sources), so components never
|
|
|
|
|
* resubscribe across renders. Equality defaults to Object.is.
|
2026-07-23 02:56:16 +08:00
|
|
|
* @param w - snapshot source (engine store, Session object, store instance).
|
2026-07-19 21:17:57 +08:00
|
|
|
* @returns the selector hook.
|
|
|
|
|
*/
|
2026-07-23 02:56:16 +08:00
|
|
|
export function bindSnapshotSelector<T>(w: HostObservable<T>): SnapshotSelectorHook<T> {
|
2026-07-19 21:17:57 +08:00
|
|
|
const subscribe = (fn: () => void) => w.subscribe(fn)
|
|
|
|
|
const getSnapshot = () => w.getSnapshot()
|
|
|
|
|
return function useSelector<S>(sel: (s: T) => S, eq?: (a: S, b: S) => boolean): S {
|
|
|
|
|
return useSyncExternalStoreWithSelector(subscribe, getSnapshot, undefined, sel, eq)
|
|
|
|
|
}
|
|
|
|
|
}
|