Files
deepseek-harness/packages/client/ui-slash
imccyu 0efc7f045e refactor(client): carve outward interfaces for the session, workspace, layout, slash, and conversation services
Feature packages now reach these domains through interface types only:
ISession/SessionFace (identity + prompt/cancel/loadOlder + the useSession
snapshot source), ISessions, IWorkspaces, ILayout, IConversation, and the
existing SlashServiceContract now actually mounted on Context.slash. The
concrete services implement their face; wire-pump and assembly entry
points stay on the classes. The provide-channel materialization and
current-projection logic moves into SessionProvideChannel so the
production service and the client test runtime share one implementation.
The workspaces service consumes sessions through the narrow SessionsPort.
2026-07-28 23:43:32 +08:00
..

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

English | 中文

Input trigger pipeline plugin: / and @ detection under the caret (word-boundary + guard-tier rules), the grouped candidate menu, and pick routing to registered sources. ctx.slash owns the source roster and resolves one SlashController per session scope (sessionOf); the conversation wiring layer drives track/arbitrate/onSpace/adjudicate on the controller. Sources receive a ClientSessionContext projection per call — sessions are always agent-backed, so the projection is the session identity alone. A source is warmed in every session controller it can reach: the roster present at scope birth warms during controller construction, and a source registered later is warmed into every live controller by the registration itself. Sources whose lexicon roll changes after warm implement subscribeLexicon(session, listener); the controller re-polls on each notification and publishes the aggregation through its lexicon snapshot store. The pipeline is command-agnostic: space/enter adjudication polls the optional matchSpace/matchEnter hooks in registration order and the first non-undefined answer wins.

Layering: src/core/ (T2) is the pure core — detectTrigger, menuReduce/seedGroups/MENU_CLOSED, exactMatch, zero React/DOM/cordis; src/client/service.ts is the shell wiring the core to the menu snapshot store, the per-hit candidate fetch (generation-gated, AbortSignal-superseded, failed sources drop silently with a console record), and the three pick paths. src/types.ts and the two contract.ts files are the frozen cross-package contract (design v4 §5.1); changes require main-thread arbitration.

MenuView renders the menu store into the conversation.input.overlay slot (list kind, session scope) and renders null while closed. The slot is owned by ui-conversation's composer entry (anchor, children declaration, lifecycle); its SlotMap type merge lives in this package's src/client/slots.ts because the dependency direction (ui-conversation → ui-slash) admits no reverse type import. Combobox pattern: focus stays in the textarea, rows pick on mousedown, the highlight rides aria-activedescendant.

The /client export surface is the plugin body (apply/inject), SlashService, MenuViewInjected, and the contract types. MenuView itself is internal — the slot registration closes over it.

Model Experience

None, as the trigger pipeline is browser presentation only — picks produce CommandClaim/ReferenceInsert data whose model-visible consequences (host command execution; inserted reference text riding an ordinary prompt) are owned by the consuming host and input-machine packages.

KV Cache effect

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

Known Limitations and Deferred Work

  • Global source layer only — session-scope source registration (per-session shadowing, ScopedLayers-alike) is designed but not enabled; the ledger tracks the trigger condition (a real per-session source need).
  • SlashCandidate.icon renders as text — MenuView drops the string into the icon slot verbatim; wiring to the design-system icon enum (iconFile five-variant family) lands when that enum ships.
  • Overlay SlotMap merge home is split from slot ownership — the conversation.input.overlay merge lives here (sole copy) while the slot's owner semantics (anchor, children declaration, lifecycle) stay with ui-conversation; the dependency direction (ui-conversation → ui-slash) forces the split, so a future dependency reshuffle should revisit it.
  • Menu group order is registration order — no explicit ordering seam across sources; acceptable while the roster is command/skill/subagent, revisit if business sources join.