dsh-client-ui-selection-add
No description
- Stars
- 0
- Language
- TypeScript
- Created
- Aug 24, 2026
- Updated
- Aug 24, 2026
Introduction
dsh-client-ui-selection-add
English | 中文
Web selection-add feature plugin: its browser half contributes a floating
Add to conversation button into the conversation-owned
conversation.input.dock slot. While a session's composer is mounted the
plugin listens for text selection; a non-empty, non-editable selection floats a
small button just above its bounding box, and clicking the button fills the
composer draft (inputActions.setDraft) without sending — the user reviews
and hits Enter (or the send button) themselves. Its host half is empty on
purpose: capturing a selection and prefilling the draft is pure browser-surface
presentation, so there is nothing for the Host to own.
The component is mounted in a session-scope list slot, so the framework
delivers the session standard kit (useSession, sessionId, useProjection,
useInput, inputActions) plus the session/input owner share and the
selection locale seat; the component only reaches for inputActions and t.
Because the button is a body-level portal, the dock entry renders nothing itself
— the composer layout is untouched.
Two behaviors worth calling out:
- Prefill, not auto-send. The click is
setDraft(text);submit()is deliberately not called, so the user can edit the quoted text before it goes to the model. (Sending on click is a one-line change inSelectionAddButton.) - Editable-field guard. Selections inside an
input,textarea, orcontenteditablenever show the button — selecting inside the composer is editing the draft, not quoting the conversation, and the button would fight the caret.
The button copy is bilingual under the selection namespace of dsh-client-locale.
The anchor capture lives in a framework-free selection.ts module
(selectionAnchorOf / isEditableField), unit-tested on its own.
Model Experience
None. Everything here is a browser interaction: the model never sees a tool,
a session event, or a schema. The plugin is a pure presentation surface over
the input machine's public inputActions face.
KV Cache effect
No invalidation. The plugin reads no session data and publishes no session events, so the conversation log, projections, and KV caches are untouched.
Known Limitations and Deferred Work
- Not scoped to the transcript body. The selection listener is
document-wide; the only guard today is the editable-field check. Selecting in the sidebar or settings shows the button because the plugin cannot identify the transcript scroll container without changingui-conversation(the no-core-change choice above). Scoping it requires either a properconversation.selection.*slot (see below) or adata-*hook the conversation skeleton already owns. - Prefill only. The user must confirm before send; there is no "send now" affordance on the floating button.
- Multi-line selections anchor above the bounding box's top (the first line); a selection that starts near the viewport top may clip the button.
- View-chat only, by construction. The button only mounts while a session composer is mounted, so it does not appear in the no-session hero. It does not distinguish which view tab is active.
Extension point (the "proper slot") upgrade
The current path rides the existing conversation.input.dock, so it requires
zero changes to ui-conversation. To make the capture a first-class extension
point instead, add a session-scope slot to ui-conversation
(packages/client/ui-conversation/src/client/apply.ts children plus the
contract/slots.ts SlotMap merge), e.g. conversation.selection.action
{ kind: 'list', scope: 'session' }, and register this plugin's entry there.
Build and publish independently (community plugin)
The package is a buildable, independently publishable DSH client plugin. It
does not need the deepseek-harness monorepo to compile: package.json,
tsconfig.json, and tsdown.config.ts are all standalone, and the
@deepseek-ai/* imports are import type only, so they resolve from
node_modules at their published versions.
Build
pnpm install
pnpm bundle # tsc (emits lib/types/**/*.d.ts) + tsdown (emits lib/*.js)
pnpm watch # tsdown --watch
Output:
lib/index.js— host half (emptyapply; the browser half does all the work)lib/invariant.js— invariant companionlib/client.js— the browser module-table bundle (window.__ModuleLoader__.load({ id, factory }))lib/types/**/*.d.ts— TypeScript declarations
Publish
pnpm bundle first (or rely on prepack, which runs it automatically), then:
npm publish # or pnpm publish
The published surface is the files list: lib/*.js (+ maps) and
lib/types/**/*.d.ts, plus cordis.patch.yml.
Install into DSH (community plugin)
This package declares the dsh.bundle manifest, so it installs via the same
channel as any community plugin (installing it from npm):
npm install dsh-client-ui-selection-add
dsh plugin --profile web add dsh-client-ui-selection-add
or, for a local build:
- Symlink/junction this directory into
$DSH_HOME/profiles/node_modules/dsh-client-ui-selection-add. - Append the host row to
$DSH_HOME/profiles/web/cordis.patch.yml:
(or just- insert: - id: ui-selection-add name: dsh-client-ui-selection-adddsh plugin addwrites it for you viadsh.bundle.patch→cordis.patch.yml).
The client half registers into the framework-owned conversation.input.dock
slot and pulls the locale service. Both are provided by the stock dsh web
build (@deepseek-ai/dsh-client-ui-slots, @deepseek-ai/dsh-client-locale,
@deepseek-ai/dsh-client-ui-conversation), so a stock dsh web build resolves
the runtime. Because these framework packages are not all baseline
module-table rows (they're separate dsh.client graph rows), a bare
community install only works against a DSH Web build that already activates
them — which is the case for upstream dsh web.
Cross-reference: deepseek-harness monorepo
This repository is the standalone home of the package. The same sources are also
wired into the deepseek-harness monorepo as
@deepseek-ai/dsh-client-ui-selection-add at packages/client/ui-selection-add;
there the tsdown preset and the three registration surfaces
(tsconfig.client.json reference, packages/bundle/web-app/cordis.patch.yml
dsh.client roster row, packages/bundle/web-app/package.json dependency) come
from the monorepo itself. This checkout builds and publishes from the
self-contained configs above instead.