From d68a58f6a414fc620e04591ece3f73eb8c995aa0 Mon Sep 17 00:00:00 2001 From: creatixchu Date: Wed, 29 Jul 2026 02:56:38 +0800 Subject: [PATCH] refactor(host): browse flow off the ./client surface (export discipline) BrowseDirectoryFlow and its injected face move to the package-internal src/client/flow.ts, mirroring -native: ./client exports only the Loader surface and the same-package spec imports the internal module directly. --- .../src/client/flow.ts | 43 +++++++++++++++++++ .../src/client/index.ts | 43 +++---------------- .../tests/client-flow.spec.tsx | 3 +- 3 files changed, 50 insertions(+), 39 deletions(-) create mode 100644 packages/host/directory-picker-browse/src/client/flow.ts diff --git a/packages/host/directory-picker-browse/src/client/flow.ts b/packages/host/directory-picker-browse/src/client/flow.ts new file mode 100644 index 0000000000..662c40de62 --- /dev/null +++ b/packages/host/directory-picker-browse/src/client/flow.ts @@ -0,0 +1,43 @@ +/** + * The browse picking occupant (package-internal; the `./client` surface + * exposes only the Loader exports). Same-package tests exercise it directly + * through this module. + */ +import { createElement } from 'react' +import type { ReactElement } from 'react' +import type { DirectoryListing } from '@deepseek-ai/dsh-client-runtime/client' +import type { Translate } from '@deepseek-ai/dsh-client-locale/client' +// Type-only: the owner contract of the directory-flow holes. +import type { DirectoryFlowOwnerProps } from '@deepseek-ai/dsh-client-ui-workspace/client' +import { DirectoryBrowser } from './DirectoryBrowser.tsx' + +/** Injected face: the browse wire calls and copy the dialog drives (bound in apply's closure). */ +export interface BrowseFlowInjected { + /** List one directory level (absent path = the Host home directory). */ + listDirectory: (path?: string) => Promise + /** Create one child directory under an existing parent. */ + createDirectory: (path: string, name: string) => Promise + /** Localized dialog copy (this package's namespace). */ + t: Translate +} + +/** + * Flow occupant: adapts the hole's owner conversation onto the browser + * dialog — a confirmed directory is the picked path, dismissal is the + * cancellation. Browse failures (unreadable targets, create conflicts) stay + * inside the dialog's own alert surfaces, so the owner's `onError` arm is + * never driven by this occupant. + * @param props - owner conversation plus the injected browse face. + * @returns the dialog element (renders nothing while closed). + */ +export function BrowseDirectoryFlow(props: DirectoryFlowOwnerProps & BrowseFlowInjected): ReactElement { + return createElement(DirectoryBrowser, { + open: props.open, + busy: props.busy, + listDirectory: props.listDirectory, + createDirectory: props.createDirectory, + t: props.t, + onOpen: props.onPicked, + onClose: props.onCancel, + }) +} diff --git a/packages/host/directory-picker-browse/src/client/index.ts b/packages/host/directory-picker-browse/src/client/index.ts index cbac191217..086c352622 100644 --- a/packages/host/directory-picker-browse/src/client/index.ts +++ b/packages/host/directory-picker-browse/src/client/index.ts @@ -7,49 +7,16 @@ * cordis.yml row; no client code branches on a capability kind. The dialog's * copy is locale-registered here — the flow package owns its own strings. */ -import { createElement } from 'react' -import type { ReactElement } from 'react' import { deferRegistration } from '@deepseek-ai/dsh-client-ui-slots' -import type { ClientContext, DirectoryListing } from '@deepseek-ai/dsh-client-runtime/client' -import type { Translate } from '@deepseek-ai/dsh-client-locale/client' -// Type-only: the SlotMap merge declaring the directory-flow holes and their owner contract. -import type { DirectoryFlowOwnerProps } from '@deepseek-ai/dsh-client-ui-workspace/client' -import { DirectoryBrowser } from './DirectoryBrowser.tsx' +import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client' +// Type-only: pulls the SlotMap merge declaring the directory-flow holes. +import type {} from '@deepseek-ai/dsh-client-ui-workspace/client' +import type { BrowseFlowInjected } from './flow.ts' +import { BrowseDirectoryFlow } from './flow.ts' /** Locale namespace owning the browser dialog's copy. */ const LOCALE_NS = 'directory-browser' -/** Injected face: the browse wire calls and copy the dialog drives (bound in apply's closure). */ -interface BrowseFlowInjected { - /** List one directory level (absent path = the Host home directory). */ - listDirectory: (path?: string) => Promise - /** Create one child directory under an existing parent. */ - createDirectory: (path: string, name: string) => Promise - /** Localized dialog copy (this package's namespace). */ - t: Translate -} - -/** - * Flow occupant: adapts the hole's owner conversation onto the browser - * dialog — a confirmed directory is the picked path, dismissal is the - * cancellation. Browse failures (unreadable targets, create conflicts) stay - * inside the dialog's own alert surfaces, so the owner's `onError` arm is - * never driven by this occupant. - * @param props - owner conversation plus the injected browse face. - * @returns the dialog element (renders nothing while closed). - */ -export function BrowseDirectoryFlow(props: DirectoryFlowOwnerProps & BrowseFlowInjected): ReactElement { - return createElement(DirectoryBrowser, { - open: props.open, - busy: props.busy, - listDirectory: props.listDirectory, - createDirectory: props.createDirectory, - t: props.t, - onOpen: props.onPicked, - onClose: props.onCancel, - }) -} - /** Required services (cordis fiber inject): the slot registry, the wire-facing workspace service, and locale. */ export const inject = ['slots', 'workspaces', 'locale'] diff --git a/packages/host/directory-picker-browse/tests/client-flow.spec.tsx b/packages/host/directory-picker-browse/tests/client-flow.spec.tsx index a3a35c7b54..5c63b00a03 100644 --- a/packages/host/directory-picker-browse/tests/client-flow.spec.tsx +++ b/packages/host/directory-picker-browse/tests/client-flow.spec.tsx @@ -6,7 +6,8 @@ import { SlotsService } from '@deepseek-ai/dsh-client-runtime/client' import type { DirectoryListing } from '@deepseek-ai/dsh-client-runtime/client' import { LocaleService } from '@deepseek-ai/dsh-client-locale/client' import type { DirectoryFlowOwnerProps } from '@deepseek-ai/dsh-client-ui-workspace/client' -import { apply, BrowseDirectoryFlow, inject } from '../src/client/index.ts' +import { apply, inject } from '../src/client/index.ts' +import { BrowseDirectoryFlow } from '../src/client/flow.ts' afterEach(cleanup)