refactor(gui): move the snapshot-store engine into the client runtime
The data layer no longer depends on the React glue package, and business plugins no longer depend on web-react at all: - The store engine (zustand vanilla + immer + persist + dev freeze), defineStore, and shallowEqual move to @deepseek-ai/dsh-client-runtime, exported from the ./client main entry — no ./store subpath survives on either package (the web-react one is deleted, none is opened on runtime). - Store products are bare snapshot sources: useSelector leaves SnapshotStore/StoreInstance and Session; every hook is composed at the binding site in web-react's renderer (per-source cached uSES binding). The SlotRendererHost sessions face carries bare observables only. - SessionProvider becomes a standard-kit seat: an entry whose children declare a session-scope slot receives the framework component as a prop, retiring the last value import of web-react from plugin packages. UseSession and the session-area types now live in ui-slots. - web-react shrinks to the shell-only React glue (renderer, providers, uSES bridge); zustand/immer belong to runtime alone; the module-table seed and tsdown externals drop the web-react/store seat. - NODE_ENV replacement is defined once in the shared tsdown client preset (browser bundles inline the engine and lost vite's define); the 3-line process.env typecheck shim moves to runtime with the engine. - Stray tsc artifacts (.js/.d.ts/.d.ts.map beside sources under src/) swept repo-wide; they shadow real sources under vitest resolution. Verified: both aggregate typecheck programs at zero; 604 client tests green; repo-wide grep for web-react/store at zero; real-host playwright run 7/7 including persist round-trip. ci: fix test/docs
This commit is contained in:
@@ -39,7 +39,6 @@
|
||||
"@deepseek-ai/dsh-client-ui-layout": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-primitives": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-ui-slots": "workspace:^",
|
||||
"@deepseek-ai/dsh-client-web-react": "workspace:^",
|
||||
"clsx": "^2.0.0",
|
||||
"react": "^18.2.0"
|
||||
},
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
import type {
|
||||
ConversationNode, ConversationSnapshot, RunningToolCall, SessionId, ToolResultNode,
|
||||
} from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-web-react'
|
||||
import type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import { IconChevronDownOutline14 } from '@deepseek-ai/dsh-client-ui-primitives'
|
||||
import type { ConvViewProps, SelectionTarget, Translate } from '../contract/views.ts'
|
||||
import type { ToolViewProps } from '../contract/toolview.ts'
|
||||
@@ -38,7 +38,7 @@ const FOLLOW_THRESHOLD = 24
|
||||
|
||||
type OpenDetails = (target: SelectionTarget) => void
|
||||
|
||||
/** web-react's UseSession is deliberately wide (dependency direction); the
|
||||
/** ui-slots' UseSession is deliberately wide (dependency direction); the
|
||||
* chat view narrows once to the runtime snapshot the binding actually feeds. */
|
||||
type UseConversation = SnapshotSelectorHook<ConversationSnapshot>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import { memo, useMemo } from 'react'
|
||||
import type { ConversationSnapshot } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-web-react'
|
||||
import type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import type { ChromeProps } from '../contract/views.ts'
|
||||
import css from './StatsLine.module.css'
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* implementation files import this, never each other.
|
||||
*/
|
||||
import type { FC } from 'react'
|
||||
import type { UseSession } from '@deepseek-ai/dsh-client-web-react'
|
||||
import type { UseSession } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import type { SessionId, ToolCallBlock } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { CallId, Translate } from './views.ts'
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* import this, never each other.
|
||||
*/
|
||||
import type { FC } from 'react'
|
||||
import type { SnapshotSelectorHook, UseSession } from '@deepseek-ai/dsh-client-web-react'
|
||||
import type { SnapshotSelectorHook, UseSession } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import type { SessionId } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
|
||||
import { useMemo, useSyncExternalStore, type ReactNode } from 'react'
|
||||
import clsx from 'clsx'
|
||||
import { shallowEqual } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { SessionId, SessionListState, SessionSummary } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import { shallowEqual } from '@deepseek-ai/dsh-client-web-react'
|
||||
import type { ConversationSlotProps } from '../contract/slots.ts'
|
||||
import type { ConvViewProps, ViewEntry } from '../contract/views.ts'
|
||||
import { InputBar } from './InputBar.tsx'
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
// share the store seat exists for) and derives the call material from the
|
||||
// session snapshot — no data of its own.
|
||||
|
||||
import { shallowEqual } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { ConversationSnapshot, ToolResultNode } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import { shallowEqual } from '@deepseek-ai/dsh-client-web-react'
|
||||
import type { DetailsSlotProps } from '../contract/slots.ts'
|
||||
import css from './DetailsPanel.module.css'
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* Module exports the factory only — a module-level handle would pin identity
|
||||
* in the module cache (a de-facto singleton surviving plugin reloads).
|
||||
*/
|
||||
import { defineStore } from '@deepseek-ai/dsh-client-web-react'
|
||||
import { defineStore } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { ChatStoreState, SelectionTarget, ViewId } from './contract/views.ts'
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
import { Context } from 'cordis'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { cleanup } from '@testing-library/react'
|
||||
import { createSnapshotStore } from '@deepseek-ai/dsh-client-web-react/src/store/index.ts'
|
||||
import { createSnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import { SlotsService, scopeOf } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { SessionId, SessionListState } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { SlotRendererHost } from '@deepseek-ai/dsh-client-web-react'
|
||||
import type { SlotRendererHost } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import { apply, inject } from '@deepseek-ai/dsh-client-ui-conversation/client'
|
||||
import type {
|
||||
ConversationInjected, DetailsInjected, EmptyStateInjected,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import { Context } from 'cordis'
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { createSnapshotStore } from '@deepseek-ai/dsh-client-web-react/src/store/index.ts'
|
||||
import { createSnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import { SlotsService } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { SessionId, SessionListState } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import { apply, inject, ToolViewRegistry } from '@deepseek-ai/dsh-client-ui-conversation/client'
|
||||
|
||||
@@ -9,8 +9,8 @@ import { cleanup, render } from '@testing-library/react'
|
||||
import { act } from '@testing-library/react'
|
||||
import type { SessionId, ToolResultNode } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { RpcId } from '@deepseek-ai/dsh-client-connection/client'
|
||||
import { bindSnapshotSelector } from '@deepseek-ai/dsh-client-web-react'
|
||||
import type { UseSession } from '@deepseek-ai/dsh-client-web-react'
|
||||
import { hookOf } from './hook.ts'
|
||||
import type { UseSession } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import { ToolViewRegistry } from '@deepseek-ai/dsh-client-ui-conversation/client'
|
||||
import type { ToolViewProps, Translate } from '@deepseek-ai/dsh-client-ui-conversation/client'
|
||||
import { MessageItem } from '../src/client/chat/MessageItem.tsx'
|
||||
@@ -85,7 +85,7 @@ describe('small branch tails', () => {
|
||||
}
|
||||
const source = { getSnapshot: () => snap, subscribe: () => () => {} }
|
||||
const view = render(
|
||||
<StatsLine sessionId={SID} useSession={bindSnapshotSelector(source) as unknown as UseSession} />,
|
||||
<StatsLine sessionId={SID} useSession={hookOf(source) as unknown as UseSession} />,
|
||||
)
|
||||
expect(view.getByText('10 tokens · 1 turns · 1 steps')).toBeTruthy()
|
||||
})
|
||||
|
||||
@@ -8,8 +8,8 @@ import { act, cleanup, fireEvent, render } from '@testing-library/react'
|
||||
import type {
|
||||
AssistantMessageNode, ConversationSnapshot, SessionId, ToolResultNode,
|
||||
} from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import { bindSnapshotSelector } from '@deepseek-ai/dsh-client-web-react'
|
||||
import type { UseSession } from '@deepseek-ai/dsh-client-web-react'
|
||||
import { hookOf } from './hook.ts'
|
||||
import type { UseSession } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import type { ChromeProps, ToolViewProps } from '@deepseek-ai/dsh-client-ui-conversation/client'
|
||||
import { ToolViewRegistry } from '@deepseek-ai/dsh-client-ui-conversation/client'
|
||||
import { StatsLine, deriveStats } from '../src/client/chat/StatsLine.tsx'
|
||||
@@ -78,7 +78,7 @@ describe('deriveStats', () => {
|
||||
|
||||
describe('StatsLine', () => {
|
||||
function props(source: { getSnapshot(): ConversationSnapshot; subscribe(fn: () => void): () => void }): ChromeProps {
|
||||
return { sessionId: SID, useSession: bindSnapshotSelector(source) as unknown as UseSession }
|
||||
return { sessionId: SID, useSession: hookOf(source) as unknown as UseSession }
|
||||
}
|
||||
|
||||
it('renders the joined stats row and hides with zero steps', () => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { cleanup, fireEvent, render } from '@testing-library/react'
|
||||
|
||||
afterEach(cleanup)
|
||||
import type { RunningToolCall, ToolResultNode } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { UseSession } from '@deepseek-ai/dsh-client-web-react'
|
||||
import type { UseSession } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import { classifyTool, toolRowModel } from '../src/client/contract/tool-call-model.ts'
|
||||
import { ToolRow } from '../src/client/chat/ToolRow.tsx'
|
||||
import { GenericToolCard } from '../src/client/chat/GenericToolCard.tsx'
|
||||
|
||||
@@ -9,8 +9,8 @@ import { act, cleanup, fireEvent, render } from '@testing-library/react'
|
||||
import type {
|
||||
AssistantMessageNode, ConversationNode, ConversationSnapshot, RunningToolCall, SessionId, ToolResultNode, UserMessageNode,
|
||||
} from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import { bindSnapshotSelector } from '@deepseek-ai/dsh-client-web-react'
|
||||
import type { UseSession } from '@deepseek-ai/dsh-client-web-react'
|
||||
import { hookOf } from './hook.ts'
|
||||
import type { UseSession } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import type { ConvViewProps, SelectionTarget } from '@deepseek-ai/dsh-client-ui-conversation/client'
|
||||
import { ToolViewRegistry } from '@deepseek-ai/dsh-client-ui-conversation/client'
|
||||
import { createChatStore } from '../src/client/stores.ts'
|
||||
@@ -79,8 +79,8 @@ function makeHarness(init?: Partial<ConversationSnapshot>) {
|
||||
const chat = createChatStore().create()
|
||||
const props: ConvViewProps = {
|
||||
sessionId: SID,
|
||||
useSession: bindSnapshotSelector(source) as unknown as UseSession,
|
||||
useStore: chat.useSelector,
|
||||
useSession: hookOf(source) as unknown as UseSession,
|
||||
useStore: hookOf(chat),
|
||||
actions: { openDetails, loadOlder },
|
||||
}
|
||||
const setSelection = (next: SelectionTarget | null): void => { chat.actions.select(next) }
|
||||
|
||||
@@ -8,7 +8,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { cleanup, render } from '@testing-library/react'
|
||||
import type { ToolResultNode } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { RpcId } from '@deepseek-ai/dsh-client-connection/client'
|
||||
import type { UseSession } from '@deepseek-ai/dsh-client-web-react'
|
||||
import type { UseSession } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import { ToolViewRegistry } from '@deepseek-ai/dsh-client-ui-conversation/client'
|
||||
import type { ConversationService, Translate, ToolViewProps } from '@deepseek-ai/dsh-client-ui-conversation/client'
|
||||
import { apply as nodeApply } from '../src/index.ts'
|
||||
|
||||
@@ -8,12 +8,11 @@
|
||||
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { cleanup, render } from '@testing-library/react'
|
||||
import { Context } from 'cordis'
|
||||
import { bindSnapshotSelector } from '@deepseek-ai/dsh-client-web-react'
|
||||
import { createSnapshotStore } from '@deepseek-ai/dsh-client-web-react/src/store/index.ts'
|
||||
import type { UseSession } from '@deepseek-ai/dsh-client-web-react'
|
||||
import { hookOf } from './hook.ts'
|
||||
import { createSnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { UseSession } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import type { ConversationSnapshot, SessionId, SessionListState } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import { apply, inject, ToolViewRegistry } from '@deepseek-ai/dsh-client-ui-conversation/client'
|
||||
import { ToolViewRegistry } from '@deepseek-ai/dsh-client-ui-conversation/client'
|
||||
import type { SelectionTarget } from '@deepseek-ai/dsh-client-ui-conversation/client'
|
||||
import { createChatStore } from '../src/client/stores.ts'
|
||||
import { AssistantMarkdown } from '../src/client/chat/AssistantMarkdown.tsx'
|
||||
@@ -32,19 +31,6 @@ function snapshotBase(): ConversationSnapshot {
|
||||
} as ConversationSnapshot
|
||||
}
|
||||
|
||||
describe('apply need() and cwd cache', () => {
|
||||
it('apply fails loud when a required service is absent', () => {
|
||||
// Call apply directly (no fiber machinery): need('sessions') on a bare
|
||||
// context throws synchronously — the loud-failure branch without the
|
||||
// fiber runner's internal rejection surface. Mount semantics (inject
|
||||
// gating) are covered by the full bench in apply-inject.spec.
|
||||
void inject
|
||||
const ctx = new Context()
|
||||
expect(() => { (apply as (c: Context) => void)(ctx) }).toThrow(/sessions service unavailable/)
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('render branch tails', () => {
|
||||
it('AssistantMarkdown reasoning row is ok-state when not the streaming tail', () => {
|
||||
const view = render(
|
||||
@@ -68,7 +54,7 @@ describe('render branch tails', () => {
|
||||
}
|
||||
const source = { getSnapshot: () => snap, subscribe: () => () => {} }
|
||||
const view = render(
|
||||
<StatsLine sessionId={SID} useSession={bindSnapshotSelector(source) as unknown as UseSession<ConversationSnapshot>} />,
|
||||
<StatsLine sessionId={SID} useSession={hookOf(source) as unknown as UseSession<ConversationSnapshot>} />,
|
||||
)
|
||||
expect(view.getByText('cache hit 0% · 15 tokens · 2 turns · 3 steps')).toBeTruthy()
|
||||
})
|
||||
@@ -90,9 +76,9 @@ describe('render branch tails', () => {
|
||||
const view = render(
|
||||
<DetailsPanel
|
||||
sessionId={SID}
|
||||
useSession={bindSnapshotSelector({ getSnapshot: () => snap, subscribe: () => () => {} }) as unknown as UseSession<ConversationSnapshot>}
|
||||
useSessions={emptyList.useSelector}
|
||||
useStore={chat.useSelector}
|
||||
useSession={hookOf({ getSnapshot: () => snap, subscribe: () => () => {} }) as unknown as UseSession<ConversationSnapshot>}
|
||||
useSessions={hookOf(emptyList)}
|
||||
useStore={hookOf(chat)}
|
||||
actions={chat.actions}
|
||||
closeDetails={vi.fn()}
|
||||
/>,
|
||||
|
||||
26
packages/client/ui-conversation/tests/hook.ts
Normal file
26
packages/client/ui-conversation/tests/hook.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Test-local selector-hook binder: the engine carries no hook since the store
|
||||
* migration (runtime is React-free); the renderer binds in production, specs
|
||||
* bind here. Delegates to web-react's bindSnapshotSelector SOURCE (same
|
||||
* with-selector uSES shim as production, so selector-level render economics —
|
||||
* a top-level snapshot swap with an unchanged slice does NOT re-render — hold
|
||||
* in Profiler-count specs). Source-relative import: the package dependency
|
||||
* edge to web-react is gone (store migration §7); tests reach the sibling
|
||||
* package the same way they reach their own src internals.
|
||||
*/
|
||||
import { bindSnapshotSelector } from '../../web-react/src/bind.ts'
|
||||
|
||||
/** Minimal observable source (engine stores and scripted fakes both satisfy it). */
|
||||
export interface HookSource<T> {
|
||||
getSnapshot(): T
|
||||
subscribe(fn: () => void): () => void
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind a selector hook over a snapshot source.
|
||||
* @param src - the source.
|
||||
* @returns a SnapshotSelectorHook-shaped hook.
|
||||
*/
|
||||
export function hookOf<T>(src: HookSource<T>) {
|
||||
return bindSnapshotSelector<T>(src)
|
||||
}
|
||||
@@ -75,8 +75,8 @@ function storeFor(b: Bench, slot: 'conversation' | 'details', sessionId: Session
|
||||
}
|
||||
|
||||
/** The host face is only built at renderSlot time; install a stub renderer once to reach it. */
|
||||
function renderHost(b: Bench): import('@deepseek-ai/dsh-client-web-react').SlotRendererHost {
|
||||
const captured = (b as unknown as { _host?: import('@deepseek-ai/dsh-client-web-react').SlotRendererHost })
|
||||
function renderHost(b: Bench): import('@deepseek-ai/dsh-client-ui-slots').SlotRendererHost {
|
||||
const captured = (b as unknown as { _host?: import('@deepseek-ai/dsh-client-ui-slots').SlotRendererHost })
|
||||
if (captured._host === undefined) {
|
||||
b.slots.install({
|
||||
renderRoot: (host) => {
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { cleanup, fireEvent, render, waitFor } from '@testing-library/react'
|
||||
import { bindSnapshotSelector } from '@deepseek-ai/dsh-client-web-react'
|
||||
import { createSnapshotStore } from '@deepseek-ai/dsh-client-web-react/src/store/index.ts'
|
||||
import type { UseSession } from '@deepseek-ai/dsh-client-web-react'
|
||||
import { hookOf } from './hook.ts'
|
||||
import { createSnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { UseSession } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import type { ConversationSnapshot, SessionId, SessionListState } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { SelectionTarget, ViewEntry } from '@deepseek-ai/dsh-client-ui-conversation/client'
|
||||
// Export discipline: packages/client/AGENTS.md.
|
||||
@@ -49,7 +49,7 @@ function listHook(rows: { id: string; title: string; cwd?: string; parentId?: st
|
||||
}])),
|
||||
current: undefined,
|
||||
} as SessionListState)
|
||||
return store.useSelector
|
||||
return hookOf(store)
|
||||
}
|
||||
|
||||
describe('ConversationRoot branches', () => {
|
||||
@@ -66,9 +66,9 @@ describe('ConversationRoot branches', () => {
|
||||
const view = render(
|
||||
<ConversationRoot
|
||||
sessionId={SID}
|
||||
useSession={bindSnapshotSelector(sessionSource(over?.snapshot)) as unknown as UseSession<ConversationSnapshot>}
|
||||
useSession={hookOf(sessionSource(over?.snapshot)) as unknown as UseSession<ConversationSnapshot>}
|
||||
useSessions={listHook(over?.rows ?? [])}
|
||||
useStore={chat.useSelector}
|
||||
useStore={hookOf(chat)}
|
||||
actions={chat.actions}
|
||||
views={{ list: () => [chatEntry], subscribe: () => () => {}, version: () => 1 }}
|
||||
send={vi.fn()}
|
||||
@@ -124,9 +124,9 @@ describe('ConversationRoot branches', () => {
|
||||
const view = render(
|
||||
<ConversationRoot
|
||||
sessionId={SID}
|
||||
useSession={bindSnapshotSelector(sessionSource()) as unknown as UseSession<ConversationSnapshot>}
|
||||
useSession={hookOf(sessionSource()) as unknown as UseSession<ConversationSnapshot>}
|
||||
useSessions={listHook([])}
|
||||
useStore={chat.useSelector}
|
||||
useStore={hookOf(chat)}
|
||||
actions={chat.actions}
|
||||
views={{ list: () => [chatEntry], subscribe: () => () => {}, version: () => 1 }}
|
||||
send={vi.fn()}
|
||||
@@ -147,9 +147,9 @@ describe('DetailsPanel branches', () => {
|
||||
return render(
|
||||
<DetailsPanel
|
||||
sessionId={SID}
|
||||
useSession={bindSnapshotSelector(sessionSource(snapshot)) as unknown as UseSession<ConversationSnapshot>}
|
||||
useSession={hookOf(sessionSource(snapshot)) as unknown as UseSession<ConversationSnapshot>}
|
||||
useSessions={listHook([])}
|
||||
useStore={chat.useSelector}
|
||||
useStore={hookOf(chat)}
|
||||
actions={chat.actions}
|
||||
closeDetails={vi.fn()}
|
||||
/>,
|
||||
@@ -183,9 +183,9 @@ describe('DetailsPanel branches', () => {
|
||||
const view = render(
|
||||
<DetailsPanel
|
||||
sessionId={SID}
|
||||
useSession={bindSnapshotSelector(source) as unknown as UseSession<ConversationSnapshot>}
|
||||
useSession={hookOf(source) as unknown as UseSession<ConversationSnapshot>}
|
||||
useSessions={listHook([])}
|
||||
useStore={chat.useSelector}
|
||||
useStore={hookOf(chat)}
|
||||
actions={chat.actions}
|
||||
closeDetails={vi.fn()}
|
||||
/>,
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
import { cleanup, fireEvent, render, screen } from '@testing-library/react'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { FC } from 'react'
|
||||
import { bindSnapshotSelector } from '@deepseek-ai/dsh-client-web-react'
|
||||
import { createSnapshotStore } from '@deepseek-ai/dsh-client-web-react/src/store/index.ts'
|
||||
import type { UseSession } from '@deepseek-ai/dsh-client-web-react'
|
||||
import { hookOf } from './hook.ts'
|
||||
import { createSnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { UseSession } from '@deepseek-ai/dsh-client-ui-slots'
|
||||
import type { ConversationSnapshot, SessionId, SessionListState } from '@deepseek-ai/dsh-client-runtime/client'
|
||||
import type { SelectionTarget, ViewEntry } from '@deepseek-ai/dsh-client-ui-conversation/client'
|
||||
// Export discipline: packages/client/AGENTS.md.
|
||||
@@ -42,7 +42,7 @@ function fakeSession(init: Partial<FakeSnapshot> = {}) {
|
||||
const store = createSnapshotStore<FakeSnapshot>({
|
||||
nodes: [], runningCalls: [], running: false, removed: false, promptError: null, ...init,
|
||||
})
|
||||
return { store, useSession: bindSnapshotSelector(store) as unknown as UseSession<ConversationSnapshot> }
|
||||
return { store, useSession: hookOf(store) as unknown as UseSession<ConversationSnapshot> }
|
||||
}
|
||||
|
||||
/** Sessions-list stub: the standard useSessions hook over a snapshot store. */
|
||||
@@ -56,7 +56,7 @@ function fakeSessions(rows: { id: string; title: string; cwd?: string; parentId?
|
||||
}])),
|
||||
current: undefined,
|
||||
} as SessionListState)
|
||||
return { store, useSessions: store.useSelector }
|
||||
return { store, useSessions: hookOf(store) }
|
||||
}
|
||||
|
||||
describe('EmptyState', () => {
|
||||
@@ -114,7 +114,7 @@ describe('ConversationRoot', () => {
|
||||
sessionId={sid('s1')}
|
||||
useSession={useSession}
|
||||
useSessions={useSessions}
|
||||
useStore={chat.useSelector}
|
||||
useStore={hookOf(chat)}
|
||||
actions={chat.actions}
|
||||
views={{
|
||||
list: () => views,
|
||||
@@ -199,7 +199,7 @@ describe('DetailsPanel', () => {
|
||||
sessionId={sid('s1')}
|
||||
useSession={useSession}
|
||||
useSessions={useSessions}
|
||||
useStore={chat.useSelector}
|
||||
useStore={hookOf(chat)}
|
||||
actions={chat.actions}
|
||||
closeDetails={closeDetails}
|
||||
/>)
|
||||
|
||||
Reference in New Issue
Block a user