Files
deepseek-harness/packages/client/web/src/app.tsx
Tianyi Cui d2dc618e3f Merge remote-tracking branch 'origin/master' into worktree/web-session-titles
# Conflicts:
#	.agents/notes/implemented/process/2026-07-20-gui-testing-system.i18n.yaml
#	packages/client/ui-conversation/tests/apply-inject.spec.tsx
#	packages/client/ui-conversation/tests/chat-stats-bash-sample.spec.tsx
#	packages/client/ui-conversation/tests/gate-branch-tails.spec.tsx
#	packages/client/ui-conversation/tests/selection-survival.spec.ts
#	packages/client/ui-conversation/tests/skeleton-branches.spec.tsx
#	packages/client/ui-conversation/tests/skeleton.spec.tsx
#	packages/client/ui-layout/tests/service.spec.ts
#	packages/client/ui-sidebar/tests/apply.spec.tsx
#	packages/client/ui-sidebar/tests/store.spec.ts
#	packages/client/ui-trajectory/tests/views.spec.tsx
#	packages/client/web/src/app.tsx
#	packages/client/web/tests/boot.spec.tsx
#	packages/host/runtime/README.md
#	packages/host/runtime/tests/host-runtime.spec.ts
2026-07-23 18:39:48 +08:00

47 lines
1.9 KiB
TypeScript

/**
* Real-UI assembly closure. Runs only after loader.settled(): the whole
* layout tree hangs off the built-in 'root' slot (ui-layout registers
* AppFrame there and renders the child slots internally) — the shell's
* render is the one ctx-level renderSlot call in the program.
*/
import type { ReactNode } from 'react'
import type { Context } from 'cordis'
import type { SessionsService } from '@deepseek-ai/dsh-client-runtime/client'
import { bindSnapshotSelector } from '@deepseek-ai/dsh-client-web-react'
import { DocumentTitle } from './DocumentTitle.tsx'
// Type-only: pulls the runtime's SlotMap declaration merge (the 'root' key) into this program.
import type {} from '@deepseek-ai/dsh-client-runtime/client'
/** Assembly inputs: the settled root ctx plus the loader's module-table read surface. */
export interface AssemblyDeps {
/** Client root context (all plugin services provided). */
ctx: Context
/** Module-table resolver (the loader's require; missing spec = throw). Kept in the seam for future shell needs. */
requireModule: (spec: string) => unknown
}
/**
* Build the renderApp factory handed to AppRoot.
* @param deps - assembly inputs.
* @returns factory producing the real UI tree (called once per AppRoot render after settled).
*/
export function buildRenderApp(deps: AssemblyDeps): () => ReactNode {
const { ctx } = deps
const sessions = ctx.get('sessions') as SessionsService | undefined
if (sessions === undefined) throw new Error('shell assembly: sessions service unavailable')
const useSessions = bindSnapshotSelector(sessions.list)
const SessionDocumentTitle = (): ReactNode => {
const title = useSessions((state) => {
const id = state.current
return id === undefined ? undefined : state.byId[id]?.title
})
return <DocumentTitle {...title === undefined ? {} : { title }} />
}
return () => (
<>
<SessionDocumentTitle />
{ctx.slots.renderSlot('root', {})}
</>
)
}