Files
deepseek-harness/packages/client/ui-workspace/README.md
imccyu ecbd8babc1 Merge remote-tracking branch 'origin/master' into mergebot/pr711
# Conflicts:
#	apps/cli/README.i18n.yaml
#	apps/cli/README.md
#	apps/cli/README.zh.md
#	apps/cli/cordis.yml
#	apps/cli/package.json
#	docs/config-catalog.md
#	packages/client/runtime/README.i18n.yaml
#	packages/client/runtime/src/client/contract/sessions.ts
#	packages/client/test-runtime/src/sessions.ts
#	packages/client/ui-workspace/README.i18n.yaml
#	packages/client/ui-workspace/README.md
#	packages/client/ui-workspace/README.zh.md
#	packages/client/ui-workspace/src/client/WorkspaceBrowser.tsx
#	packages/client/ui-workspace/src/client/tree.ts
#	packages/client/ui-workspace/tests/apply.spec.ts
#	packages/client/ui-workspace/tests/tree.spec.ts
#	packages/host/apiproxy/README.i18n.yaml
#	packages/host/apiproxy/src/api-proxy.ts
#	packages/host/apiproxy/src/api/index.ts
#	packages/host/apiproxy/tests/client-handler.spec.ts
#	packages/host/apiproxy/tests/rpc-schemas.spec.ts
#	pnpm-lock.yaml
2026-07-31 01:28:15 +08:00

4.8 KiB

@deepseek-ai/dsh-client-ui-workspace

English | 中文

Shared Workspace browser and picker plugin. WorkspaceBrowser fills the sidebar's sidebar.workspaces slot, while WorkspacePicker fills the page-local Session Intent hero's conversation.hero.workspace slot; both surfaces use the same Workspace menu and creation flow.

The browser renders grouped or flat Session rows from the global runtime hooks and owns the Workspace create/rename and in-Workspace reorder flows. A non-blank search query replaces either browsing mode with one flat result list: case-insensitive title and Workspace substring matches appear immediately, while a 250 ms debounced Host request adds ranked current-conversation content matches and snippets. The English search input and its defensive request path remove NUL, cap the query at the wire schema's 500 UTF-16 code units without splitting a surrogate pair, and preserve the existing debounce and cancellation behavior. Each new query aborts the preceding request; a failed content search leaves metadata matches visible with a warning. The list is capped at 20, asks the user to narrow broader queries, and opens the selected Session without clearing the query or jumping to a specific event.

The picker lists real Host Workspace entities through the global useWorkspaces hook. Selecting a Workspace invokes the slot owner's onPick callback to retarget the frontend Session object. Each registration declares a directory-flow child hole (single kind: conversation.hero.workspace.directoryFlow / sidebar.workspaces.directoryFlow) that the composed picker package's client half fills with its picking interaction — the -native backend's renderless OS-chooser driver today, an in-app browsing dialog under a -browse composition. The flat Open local folder... action renders only while the surface's hole is occupied (occupancy read per menu render; an empty hole means the composition has no picking affordance — the seam's documented no-flow default). This package owns the trigger and the adoption: the occupant reports one picked path per open through the hole's owner conversation (open/busy/onPicked/onCancel/onError), and the owner adopts it through the object layer, selecting the committed Workspace only after its list projection has refreshed; cancellation is silent, and errors land in the retryable folder dialog whose Choose again reopens the flow. Create a new workspace retains the name dialog and disables names already present in that list, while the Host remains authoritative for concurrent or non-UI callers. The runtime Session and Workspace services own materialization. The Workspace row's Delete action opens a confirmation that states the retention boundary, blocks duplicate submission, and keeps failures open; success removes the group while its Sessions remain under Ungrouped. The Session row's Rename action opens the same browser-owned dialog pattern prefilled with the row's display title: no client-side conflict rule exists (the host normalizes and may reject with title-invalid, rendered in the dialog alert), and confirming an unchanged title is deliberately allowed — it pins the current automatic title against regeneration.

The Session row's Fork action forks at the source's last completed turn, increments the inherited persisted title on the client, and then opens the child; a trailing ASCII or fullwidth parenthesized number is incremented in the same style, while an unnumbered title gets (1) appended. The source and child always appear as peer rows within a workspace group, with lineage retained only as session data. A fork or rename failure leaves the current selection unchanged; after a rename failure, the created child remains in the list.

Both target slots are declared by other plugins, so apply registers through declaration-aware deferral and re-registers after a declaring slot is restored.

Model Experience

None, as the picker is browser chrome; nothing here reaches a model request.

KV Cache effect

None; this package neither assembles nor sends a provider request.

Known Limitations and Deferred Work

  • No fuzzy content search or event deep links — the content backend uses literal token/phrase matching, and selecting a result opens the Session rather than the matching event.
  • No Session deletion control — the Session menu's Delete row remains visual-only; Workspace registration deletion does not delete Sessions.
  • Native folder selection depends on the local Host carrier — under the -native composition, fixture-only or remote browser deployments cannot open a local operating-system dialog; platform failures are shown in a retryable modal. Remote-capable picking is the -browse composition's in-app flow.