design-platform.css declared four --dsw-alias-scrollbar-* tokens in both palettes that no rule read, so every scrolling region rendered the user agent's own scrollbar and the dark theme showed a light native bar against dark surfaces. The symptom that surfaced the gap was in the sidebar: the workspace browser's session list is its only scrolling region, and each row's trailing content (the relative timestamp, and the hover action buttons that replace it) is `flex: none` flush against the row's 8px right padding, so an overlaid scrollbar painted on top of the timestamp. ui-theme/styles/scrollbar.css becomes the sole consumer of the four tokens, imported by the web shell's base.css after design-platform.css because it reads that sheet's tokens. The rules sit on `body`, not `html`: the alias tokens are declared on `body`, custom properties inherit only downward, and from `html` they resolve to the guaranteed-invalid value with scrollbar-color computing to `auto`. scrollbar-width and scrollbar-color are declared on `body, body *` rather than inherited, because inheritance would carry the color already substituted at `body` and an elevated surface could not retint its own thumb; scrollbar-width does not inherit at all. Both the standard properties and the ::-webkit-scrollbar pseudo-elements read one indirection pair bound to the l1 tokens, so an elevated surface rebinds that pair to the l2 tokens once and retints both renderings. The command popup, slash menu, model-select panel, and settings panel do so, which gives the l2 tokens their first consumers. WorkspaceBrowser's `.list` declares scrollbar-gutter: stable, keeping the bar beside the rows. `stable` rather than `auto` so the reservation holds when the list is short enough not to scroll: expanding a workspace group would otherwise shift every row sideways at the moment it starts scrolling.
@deepseek-ai/dsh-client-ui-command
English | 中文
Client command surface (ctx.command): the session-keyed command-directory cache, the / command source with matchSpace/matchEnter adjudication hooks, three-kind dispatch (execute / popupSelect / leadingInput), and the popupSelect registration face for business packages. Contract: the web command surfaces Agent Note.
src/client/contract.ts is the frozen business face: CommandServiceContract.register(name, spec) is everything a business package consumes; CommandUiSpec{options, onSelect} keeps popup data self-served — the shell component is this package's and business never sees it. Command kinds derive per dispatch, never per registration: a host descriptor with input is leadingInput, a registered CommandUiSpec is popupSelect, everything else is execute.
CommandDirectory (src/client/directory.ts) is the one wire-derived cache, keyed by session: every session is agent-backed, so command.list({sessionId}) is the only address shape and the source's scope-birth warm hook prewarms the session's entry. Entries are soft-invalidated by the commands/changed typed event (old snapshot serves while the repull flies), hard-invalidated by connection/reset, epoch-guarded so a superseded pull can never overwrite a newer one. matchSpace answers synchronously from this cache only; matchEnter strong-waits it on the SubmitAttempt signal and rejects on warmup failure — a / line is never silently downgraded to a plain prompt.
PopupSelectController (src/client/popup.ts) is the headless shell state: PopupSelectView self-registers into conversation.input.overlay (the SlotMap key is ui-conversation's; this package pulls the declaration in with a type-only import — no runtime edge). The shell is a transient layer holding focus while open; token-segment consumption after onSelect runs both branches through consumeTokenSegment (menu-path span CAS, enter-path bare-token equality) against the draft face the wiring layer binds via bindDraft.
The /client export surface is the plugin body (apply/inject), CommandService, the directory and popup classes with their state types, and the frozen contract types; the shell component itself is internal to the overlay registration.
Model Experience
Indirectly, through the host command.execute RPC this package's dispatch and claim.submit paths trigger: a matched command's handler mutates host domain state that other packages project into the next request (the /plan handler flips plan mode, whose owning package injects its plan:policy system-prompt section), while the command line itself, the detached result, and every menu/notice rendering stay client-side and never enter the session log.
KV Cache effect
None directly; this package neither assembles nor sends a provider request. Command handlers it triggers may change what the owning host packages contribute to the next request's system prompt (a section appearing or disappearing replaces earlier request tokens and invalidates the provider prefix from that point), but that effect is owned and documented by each command's host package.
Known Limitations and Deferred Work
- The popupSelect shell has no shipped business consumer — model selection (host
selectModel) is the design's reference case and lands with its own feature work; until then the shell is exercised by package tests only. - Detached-result notices fall back to the console off-session — the fire-and-forget paths route results to the triggering session's composer via
SessionInput.notify; after session teardown the console line is the only remaining surface.