2026-07-28 22:22:14 +08:00
|
|
|
/**
|
|
|
|
|
* Browser half of the browse directory-picker backend: fills ui-workspace's
|
|
|
|
|
* two directory-flow holes with the in-app Select Workspace Directory dialog
|
|
|
|
|
* (figma `Harness` 813-23126 family), driving the node half's
|
|
|
|
|
* `host.listDirectory`/`host.createDirectory` primitives. Mounting this
|
|
|
|
|
* package therefore composes both sides of the browse interaction with one
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2026-07-29 02:56:38 +08:00
|
|
|
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'
|
2026-07-28 22:22:14 +08:00
|
|
|
|
|
|
|
|
/** Locale namespace owning the browser dialog's copy. */
|
|
|
|
|
const LOCALE_NS = 'directory-browser'
|
|
|
|
|
|
|
|
|
|
/** Required services (cordis fiber inject): the slot registry, the wire-facing workspace service, and locale. */
|
|
|
|
|
export const inject = ['slots', 'workspaces', 'locale']
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Client plugin body: register the dialog's dictionaries and the browse flow
|
2026-08-05 23:38:29 +08:00
|
|
|
* into both directory-flow holes through `slots.inject()` because the
|
|
|
|
|
* ui-workspace entries may activate later or replace their declarations.
|
2026-07-28 22:22:14 +08:00
|
|
|
* @param ctx - client root context.
|
|
|
|
|
*/
|
|
|
|
|
export function apply(ctx: ClientContext): void {
|
|
|
|
|
ctx.effect(() => {
|
2026-07-29 04:12:44 +08:00
|
|
|
// The two dictionaries land as a unit: if the second registration hits a
|
|
|
|
|
// rival owner of the namespace, the first rolls back before the throw —
|
|
|
|
|
// a failed activation must not squat the namespace's other locale.
|
|
|
|
|
const disposers: (() => void)[] = []
|
|
|
|
|
const dictionaries: [locale: string, dict: Record<string, string>][] = [
|
|
|
|
|
['zh', {
|
2026-07-28 22:22:14 +08:00
|
|
|
'browser.title': '选择工作区目录',
|
|
|
|
|
'browser.home': '主目录',
|
|
|
|
|
'browser.newFolder': '新建文件夹',
|
|
|
|
|
'browser.folderName': '文件夹名称',
|
|
|
|
|
'browser.createIn': '在"{name}"中新建文件夹',
|
|
|
|
|
'browser.untitledFolder': '未命名文件夹',
|
|
|
|
|
'browser.create': '创建',
|
|
|
|
|
'browser.cancel': '取消',
|
|
|
|
|
'browser.open': '打开',
|
|
|
|
|
'browser.editPath': '编辑路径',
|
|
|
|
|
'browser.loading': '加载中…',
|
2026-07-29 03:53:48 +08:00
|
|
|
'browser.truncated': '文件夹过多,仅显示开头部分。',
|
2026-07-29 12:52:59 +08:00
|
|
|
'browser.showHidden': '显示隐藏文件',
|
2026-07-29 04:12:44 +08:00
|
|
|
}],
|
|
|
|
|
['en', {
|
2026-07-28 22:22:14 +08:00
|
|
|
'browser.title': 'Select Workspace Directory',
|
|
|
|
|
'browser.home': 'Home',
|
|
|
|
|
'browser.newFolder': 'New folder',
|
|
|
|
|
'browser.folderName': 'Folder name',
|
|
|
|
|
'browser.createIn': 'New folder in "{name}"',
|
|
|
|
|
'browser.untitledFolder': 'Untitled folder',
|
|
|
|
|
'browser.create': 'Create',
|
|
|
|
|
'browser.cancel': 'Cancel',
|
|
|
|
|
'browser.open': 'Open',
|
|
|
|
|
'browser.editPath': 'Edit path',
|
|
|
|
|
'browser.loading': 'Loading…',
|
2026-07-29 03:53:48 +08:00
|
|
|
'browser.truncated': 'Too many folders to list; only the beginning is shown.',
|
2026-07-29 12:52:59 +08:00
|
|
|
'browser.showHidden': 'Show hidden files',
|
2026-07-29 04:12:44 +08:00
|
|
|
}],
|
2026-07-28 22:22:14 +08:00
|
|
|
]
|
2026-07-29 04:12:44 +08:00
|
|
|
try {
|
|
|
|
|
for (const [locale, dict] of dictionaries) disposers.push(ctx.locale.register(LOCALE_NS, locale, dict))
|
|
|
|
|
} catch (error) {
|
|
|
|
|
for (const dispose of disposers.reverse()) dispose()
|
|
|
|
|
throw error
|
|
|
|
|
}
|
2026-07-28 22:22:14 +08:00
|
|
|
return () => { for (const dispose of disposers) dispose() }
|
|
|
|
|
}, 'directory-picker-browse: dialog dictionaries')
|
|
|
|
|
|
|
|
|
|
const injected = (): BrowseFlowInjected => ({
|
2026-07-29 06:19:18 +08:00
|
|
|
listDirectory: (path, signal) => ctx.workspaces.listDirectory(path, signal),
|
2026-07-28 22:22:14 +08:00
|
|
|
createDirectory: (path, name) => ctx.workspaces.createDirectory(path, name),
|
|
|
|
|
t: ctx.locale.bind(LOCALE_NS),
|
|
|
|
|
})
|
2026-08-05 23:38:29 +08:00
|
|
|
// Both declaration lifetimes must be live before the pair installs; the
|
|
|
|
|
// generator makes the two registrations one transactional effect. The
|
|
|
|
|
// outer/inner nesting order is arbitrary; neither hole has precedence.
|
|
|
|
|
ctx.slots.inject('conversation.hero.workspace.directoryFlow', () =>
|
|
|
|
|
ctx.slots.inject('sidebar.workspaces.directoryFlow', function* () {
|
|
|
|
|
yield ctx.slots.register({
|
|
|
|
|
name: 'conversation.hero.workspace.directoryFlow', inject: injected,
|
|
|
|
|
}, BrowseDirectoryFlow)
|
|
|
|
|
yield ctx.slots.register({
|
|
|
|
|
name: 'sidebar.workspaces.directoryFlow', inject: injected,
|
|
|
|
|
}, BrowseDirectoryFlow)
|
|
|
|
|
}))
|
2026-07-28 22:22:14 +08:00
|
|
|
}
|