# dsh-add-to-chat [简体中文](README.md) Select text in a DSH assistant reply and choose **Add to chat**. The plugin keeps the selection as a separate annotation and submits it as plugin-source context only when the user sends their next message. ## Demo 1. Select text in an assistant reply, then choose the floating **Add to chat** action. ![Add to chat action after selecting text](docs/images/add-to-chat-selection-action.png) 2. Add as many annotations as needed. The composer remains plain text; hover the “x annotations” pill to read and remove individual selections. ![Hover preview of two separate annotations](docs/images/add-to-chat-draft-contexts.png) 3. Keep writing normally and send. The annotations travel as independent context for that submission, are consumed after a successful send, and return if the submission fails. ![Hover preview of a submitted annotation above its user message](docs/images/add-to-chat-submitted-annotation.png) ## Features - Browser-only DSH client plugin; no Host service or product-specific integration. - Each selection stays within one assistant reply; multiple annotations can be added in succession. - Triple-click paragraph selection remains available when its trailing newline ends at a timestamp's start after empty action icons. Floating controls stay outside text selection, including on the last paragraph with existing annotations. Selections containing another message or toolbar text remain excluded. - Does not send automatically. The user can edit ordinary draft text or send annotations without text. - The “x annotations” hover card shows the full source text and supports individual or bulk removal. - Previews use vertical quote lines, small corners, and light shadows without repeated headings or numbering. The bulk-remove × is centered in a 16px white circle that stays round under host corner smoothing and turns light gray when the remove button is hovered. It overlays the annotations control without reserving width or animating, appears when the control is hovered or keyboard-focused, and stays visible on touch screens. - Draft annotation previews open above the pill when space permits, fall back below otherwise, and sit above the composer so details and remove buttons remain accessible. - It never writes Markdown, hidden nodes, or chips into the Lexical editor, so Backspace cannot remove an annotation by accident. - A blue teardrop marker with a white number stays above the selection's right edge without a text popup. Numbers follow the current annotation order and update after removal. Read the original text in the annotations card above the composer. - Interface copy follows the active DSH locale, supports Chinese and English, and refreshes immediately after a language switch. ## Compatibility The plugin relies on an assistant body marked with `data-dsh-message-role="assistant"`, the `sessions.list` catalog, the `conversation.draftContexts` submission service, and the DSH locale service. It does not depend on page CSS classes, a Host service, or product-specific code. Multiple conversation views use the `data-conversation-session` ancestor of each assistant reply and composer. Each composer displays its own pending annotations. Quotes record separate source and target sessions, so a side conversation cannot accidentally attach its references to the main conversation. Failed submission restores references only to their original target; reopening that conversation restores its draft references. Legacy single-conversation pages support the catalog's `current` field or the unique `retainedBy.mainView` owner in DSH `0.2.0-rc.1`. Catalog order and background ownership never select a target. Multiple composers without explicit conversation markers disable the default add action. ## Selection action extensions Client plugins access `chatSelection` through `ctx.inject(['chatSelection'], ...)`. `registerAction` extends the existing selection popup and returns a disposer that the consumer releases on unload. Rejected asynchronous actions show localized retry feedback and preserve the selection; consumers own detailed error reporting. ```js const off = ctx.chatSelection.registerAction({ id: 'example-side-chat', label: () => localizedLabel, run: async selection => { const targetSessionId = await openSideChat(selection.sourceSessionId) ctx.chatSelection.addQuote({ ...selection, targetSessionId }) }, }) ``` Selections include only the actual `sourceSessionId` and selected `text`, without inferred message sequence numbers. `addQuote` requires explicit source and target sessions and nonempty text; it queues context without creating a session or sending a prompt. Import types from `dsh-add-to-chat/client-api`. Consumers using a renamed Cordis package declare the same service on their own Context. This plugin does not import product-specific side-chat code. ## Install ```sh dsh plugin --profile web add github:choco9527/dsh-add-to-chat ``` For DSH Desktop, install into the active Desktop profile. The Electron renderer uses the same DSH Web client module graph. ## Development ```sh corepack pnpm install corepack pnpm run check dsh plugin --profile web add . ``` Restart the selected DSH profile after installation. Do not also manually add this plugin id to a profile `cordis.patch.yml`: the bundle already registers itself. `corepack pnpm test` loads the real client plugin and checks both catalog APIs, multiple-composer isolation, explicit quote targets, accepted submission, failed-submission recovery, and asynchronous extension feedback. It does not launch a model or access real conversations. TypeScript uses browser bundler resolution to read Cordis declarations with extensionless imports; emitted modules remain ESM. Browser regression: with Playwright installed, run `node --test test/preview.browser.mjs`. Set `PLAYWRIGHT_MODULE` to use an alternate module entry and `CHROME_EXECUTABLE` to use a specific Chrome executable. The test loads the real plugin into an isolated page and checks composer layers 7/9, narrow-window fallback, triple-click paragraph selection, cross-message exclusion, light and dark styling, and hover, keyboard, and touch removal without accessing real conversations. ## License MIT