dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
A gap-only extension for the DSH web composer's file intake.
DSH core now ships the complete attachment pipeline:
| Concern | Owner |
|---|---|
| Composer, intake validation, image limits, notices | @deepseek-ai/dsh-client-ui-conversation |
| Streaming upload, progress, cancel, retry | @deepseek-ai/dsh-client-file-upload |
| Draft attachment rail, file cards, drop overlay, lightbox | @deepseek-ai/dsh-client-ui-attachment |
| Verbatim storage, receipt admission, model-facing handle text | @deepseek-ai/dsh-attachment*, ctx.fileUploads |
Version 1 of this plugin predated all of that. It duplicated the feature — its
own chip rail, its own full-screen drop mask, its own toast, plus a drops/
directory and an agent/pre-step middleware that injected @drops/...
references — and it registered its document drop listener in the capture
phase with stopPropagation(). Because capture runs before the core's
bubble-phase listener, the core never received a drop: enabling the plugin
disabled the built-in drag & drop. That is why the profile carried
- id: dsh-file-drop / disabled: true.
Exactly one thing the core does not do: expanding a dropped folder into its individual files.
A dropped folder is walked with webkitGetAsEntry() (bounded to 2000 files /
16 levels), and the collected files are re-dispatched as an ordinary file-only
drop event — so the core's validation, limits, notices, rail, upload path and
progress all run exactly as if those files had been dropped directly.
DSH 0.1.7 added folder detection but made folders a desktop-only feature. In
ui-conversation's addFiles():
if (bridge === void 0 && directory) return t("attachment.directoryDesktopOnly")
where bridge is globalThis.__DSH_HOST_PATHS__. In a browser that global is
never set — no shipped package assigns it — so a dropped folder is rejected
with the notice "folders can only be added in the desktop app; add individual
files in the browser". On desktop the folder becomes an @path/ reference
chip, not expanded files.
This plugin intentionally overrides that browser policy. That is a product decision, not an oversight:
The override is safe because the hand-off is a synthetic file-only drop:
droppedDirectories() calls item.webkitGetAsEntry() and skips anything that
is not isDirectory === true, so the expanded files take the core's ordinary
upload path with no directory semantics attached.
lib/index.js is an intentional no-op with an empty
inject list, so this plugin cannot break profile boot or interfere with
ctx.fileUploads / ctx.attachments.drops/ and no
@drops/... references are injected. Natively attached files already reach
the model as a read-only host path handle, which is the core's own contract.Developed and verified against DSH 0.1.6-alpha.2, re-checked against 0.1.7-alpha.1 (where the folder contract changed as described above), and re-checked again against 0.2.0-rc.1.
The plugin touches only two surfaces, and both are unchanged in 0.2.0:
[data-composer-card] input[type="file"])drop event, and the core's own bubble-phase listener that
receives the re-dispatched hand-offThe gate this plugin overrides is byte-for-byte the same in 0.2.0, in
ui-conversation's addFiles():
const bridge = hostPathBridge();
// ...
if (bridge === void 0 && directory) return t("attachment.directoryDesktopOnly");
Also re-verified against the shipped 0.2.0 bundle:
| Surface | 0.2.0-rc.1 |
|---|---|
globalThis.__DSH_HOST_PATHS__ (read by hostPathBridge()) | present in ui-conversation |
core drop listeners | still bubble phase (ui-attachment, ui-workspace); no capture-phase listener was added |
webkitGetAsEntry folder walk | still the supported entry API |
0.2.0 adds installDesktopDirectoryPicker to the desktop main process, but
that serves the workspace directory picker (workspace-controller's
directoryPicker.createDirectory), not the composer's folder intake. It does
not affect this plugin.
dsh plugin --profile web add <path-to-this-directory>
Two profile-level facts must hold for the plugin to activate:
~/.dsh/profiles/web/cordis.patch.yml must carry the force-enable row:
- id: dsh-file-drop / disabled: false.~/.dsh/profiles/web/.dsh-market/state.json must not list
dsh-file-drop in its disabled array. The plugin market keeps its own
disable list there and re-asserts it on every boot, so a stale entry
holds the row down even when the patch layer enables it.Keep the force-enable row outside any # <plugin>: begin/end marker block —
those blocks are rewritten by the plugin that owns them.
Because the browser half is only served for a loader row with a live fiber, a restart (or a toggle from the plugin market panel plus a page refresh) is required after changing enablement.
node test/package.test.mjs # manifest + both artifact shapes
node test/logic.test.mjs # drop gating and the bounded folder walk