dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
DeepSeek Harness 的 Web 插件:在输入框添加截图按钮,截屏并作为草稿图片插入,仅当模型支持视觉时显示。
English | 中文
This package adds a screenshot button to the DSH Web composer tool row. Clicking it asks the browser to share a surface (whole screen, a window, or a tab), captures one decoded frame, and inserts it as a draft image in the composer attachment rail — the same image path used by paste and drag-and-drop. The button is rendered only when the active session's model supports image input, so a text-only model never sees the control. It makes no model-visible prose change and owns no server state.
Mount this plugin alongside ui-conversation and ui-model-selection. A camera button then appears in the conversation.input.right tool row, immediately before the model selector, for any session whose effective model advertises image input. A session with a text-only model, no resolved model, or an unlisted model renders nothing.
Clicking the button calls getDisplayMedia; the user picks a surface in the browser's share dialog. The first decoded frame is drawn to a canvas and exported as a PNG, registered on the conversation service as a browser-owned draft image, and appended through the public inputActions.addImages path so it shows in the composer attachment rail exactly like a pasted or dropped image. The capture stream is stopped after one frame.
If the user cancels or the runtime lacks display capture, the button does nothing. A captured file the admission path refuses is released so no orphan draft remains.
The button is a conversation.input.right entry registered with ctx.slots.register. The inject face closes over the conversation service for draft-image registration (createDraftImages / releaseDraftImage) and over the modelDirectories service for the session's shared model directory. The component reads the directory store through useSyncExternalStore, and a pure predicate (modelSupportsImage) decides visibility from the effective current model's inputModalities. It collaborates with other plugins only through cordis services — no cross-plugin value imports. The screenshot is captured in capture.ts, which waits for play() plus one presented frame (requestVideoFrameCallback, with a poll-and-timeout fallback) so the sample is not a black frame.
Read these pages when the screenshot surface is not enough.
conversation.input.right slot, the draft-image controller, and the composer.This plugin is a thin UI add-on, so it deliberately delegates its two real jobs — inserting the image, and knowing whether the model can see it — to the packages that already own them. It reaches them through cordis services, never by importing another plugin's code (client bundles forbid cross-plugin value imports, so collaboration is by contract).
ui-conversation (the conversation service)The composer and its attachment rail are owned by ui-conversation. Its ConversationController (ctx.conversation) is the single owner of the browser draft-image registry: createDraftImages allocates the ComposerAttachment (id, preview URL, dimensions) that the rail renders and that submission serializes, and releaseDraftImage cleans it up. Re-implementing a second image-intake path here would fork the validation, preview-lifecycle, and submit-serialization logic, and the two paths could disagree about which objects are live. The button therefore routes the captured PNG through that one registry plus the session's inputActions.addImages, exactly as paste and drag-and-drop do.
ui-model-selection (the modelDirectories service)ui-model-selection's ModelDirectoryResolver (ctx.modelDirectories) is the only place that knows the effective current model and its inputModalities: it merges the host-generation catalog (session.modelCatalog) with the session's durable modelSelection projection into one reactive directory that both the /model popup and the composer model seat share. A composer tool-row slot receives no model information at all, and the composer must not import ui-model-selection (the dependency is one-way — ui-model-selection → ui-conversation, never back). So this plugin reads that same directory service; loading its own catalog would double the fetch and the selection projection and could disagree with the model seat about which model is active.
The result is one source of truth for both concerns across the composer, the model seat, and this button.
Nothing is added to the model context by this plugin itself. The screenshot the user takes becomes an image part of the user's next message, delivered through the existing attachment-admission path; the model sees it as ordinary user image content.
Image tokens are billed per the provider's image accounting only when a message carrying the screenshot is submitted. The button and its own copy add no tokens.
None. The screenshot rides a new user message; it does not alter the stable prompt prefix, so it does not by itself invalidate the KV cache. A message that admits the image extends the history tail like any other message.
These are the current package constraints.
getDisplayMedia; it is unavailable in non-browser runtimes and requires a secure context (localhost is secure).None.
@deepseek-ai/cordis, plus the host-injected client services
@deepseek-ai/dsh-api-session-controller, @deepseek-ai/dsh-client-locale,
@deepseek-ai/dsh-client-ui-conversation, @deepseek-ai/dsh-client-ui-model-selection,
@deepseek-ai/dsh-client-ui-renderer, @deepseek-ai/dsh-client-ui-session. These are resolved from the
host's client module table at runtime, not installed by this package.model.inputModalities / vision-capable). A text-only model, an unresolved model, or an unlisted model
renders no button.dsh plugin --profile tmp add github:cczzyy-cn/dsh-ui-screenshot#v0.1.1
# Profile cordis.patch.yml: - insert: { id: client-ui-screenshot, name: '@deepseek-ai/dsh-client-ui-screenshot' }
dsh profile start tmp & # camera icon appears only for a vision-capable model
dsh plugin --profile tmp rm @deepseek-ai/dsh-client-ui-screenshot
dsh profile stop tmp # clean profile exit, no residue