Merge remote-tracking branch 'origin/master' into worktree-guifork

This commit is contained in:
imccyu
2026-07-28 16:12:12 +08:00
237 changed files with 3140 additions and 1659 deletions

View File

@@ -5,8 +5,8 @@
import { Component, useSyncExternalStore, type FC, type ReactNode } from 'react'
import {
SlotOwnershipError, StaleAuthorizationError,
type ChainRenderOpts, type RenderOpts, type SessionMaybeProvideInfo, type SessionProvideInfo,
type SlotRenderer, type SlotRendererHost, type SlotScope, type StoredEntry,
type ChainRenderOpts, type HostObservable, type RenderOpts, type SessionMaybeProvideInfo,
type SessionProvideInfo, type SlotRenderer, type SlotRendererHost, type SlotScope, type StoredEntry,
} from '@deepseek-ai/dsh-client-ui-slots'
import {
HostContext, SessionMaybeProvider, SessionProvider, SlotAssemblyError, maybeObservableHook,
@@ -96,7 +96,26 @@ function runInject(entry: StoredEntry, info: SessionMaybeProvideInfo | undefined
const args: unknown[] = []
if (info !== undefined) args.push(info.sessionId)
if (actions !== undefined) args.push(actions)
return (inject as (...args: unknown[]) => InjectedProps)(...args)
return bindInjectHooks((inject as (...args: unknown[]) => InjectedProps)(...args))
}
/**
* Bind an inject face's reserved `hooks` compartment (bare observable
* sources, see HooksSources) into `use<Name>` selector hooks — the
* registrant-private twin of the provide-bundle binding in standardKit.
* Runs once per cached inject result; hook identity rides observableHook's
* per-source cache.
*/
function bindInjectHooks(face: InjectedProps): InjectedProps {
const sources = face['hooks']
if (sources === undefined) return face
const { hooks: _hooks, ...rest } = face
const bound: InjectedProps = rest
for (const [name, source] of Object.entries(sources as Record<string, HostObservable<unknown>>)) {
const hookName = `use${name[0]?.toUpperCase() ?? ''}${name.slice(1)}`
bound[hookName] = observableHook(source)
}
return bound
}
function cachedRootInject(entry: StoredEntry, actions: object | undefined): InjectedProps {

View File

@@ -123,9 +123,9 @@ const projectionHookCache = new WeakMap<SessionMaybeProvideInfo, (
*/
export function SessionMaybeProvider({ children }: { children: ReactNode }) {
const host = useHost()
const id = observableHook(host.sessions.current)(s => s)
const info = observableHook(host.sessions.provideInfo)(s => s)
return (
<BindingContext.Provider value={host.sessions.maybeProvideInfo(id)}>
<BindingContext.Provider value={info}>
{children}
</BindingContext.Provider>
)
@@ -140,17 +140,17 @@ export interface SessionProviderProps {
}
/**
* Framework-wired session area: subscribes to the host's current-session
* source, resolves the session cell, and remounts the body under
* `key={sessionId}` so a session switch rebuilds the session subtree. This
* dependency-inverted layer uses plain string ids; `PropsRuntime` applies the
* branded type at the component boundary.
* Framework-wired session area: subscribes to the host's current provide
* source and remounts the body under `key={sessionId}` so a session switch
* rebuilds the session subtree. This dependency-inverted layer uses plain
* string ids; `PropsRuntime` applies the branded type at the component
* boundary.
*/
export function SessionProvider({ empty, children }: SessionProviderProps) {
const host = useHost()
const id = observableHook(host.sessions.current)(s => s)
const info = id === undefined ? undefined : host.sessions.provideInfo(id)
if (id === undefined || info === undefined) return <>{empty?.() ?? null}</>
const info = observableHook(host.sessions.provideInfo)(s => s)
const id = info.sessionId
if (id === undefined) return <>{empty?.() ?? null}</>
return (
<BindingContext.Provider value={info} key={id}>
{children(id)}