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.
This commit is contained in:
creatixchu
2026-07-29 02:56:38 +08:00
parent 882e16fdf0
commit d68a58f6a4
3 changed files with 50 additions and 39 deletions

View File

@@ -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<DirectoryListing>
/** Create one child directory under an existing parent. */
createDirectory: (path: string, name: string) => Promise<string>
/** 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,
})
}

View File

@@ -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<DirectoryListing>
/** Create one child directory under an existing parent. */
createDirectory: (path: string, name: string) => Promise<string>
/** 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']

View File

@@ -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)