dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
让超大图片以可读分辨率进入模型视野,而不是被压成一张缩图:准入前按附件存储允许的上限把大图切成多张切片(8000x8000 的截图会变成 20 张 2000x1600),小字得以保留。以替换本地附件存储的方式实现,不新增任何配置项。
Upload a screenshot, a photographed document, or a long comic page and the model reads the small text in it. Without this plugin it does not: one request image carries at most 1024 vision tokens, so the harness rescales anything larger before the model sees it, and an 8000x8000 screenshot arrives as a ~1300px-wide picture with the text already gone.
This is not an official DeepSeek product and does not imply DeepSeek endorsement.
The plugin replaces the attachment store's admission step. When a prompt carries an image too large to survive the harness' own downscaling, it cuts the image into adjacent regions and submits each region as its own image, in the same message. Every region is then transmitted at its own resolution, so the model sees the source at the resolution it was uploaded at instead of at the resolution one image can hold.
Measured on a real boot (DSH 0.2.0-rc.2, desktop host, an 8000x8000 PNG of small text):
| as one image | tiled (default settings) | |
|---|---|---|
| what the model receives | 2048x2048, re-encoded | 20 regions of 2000x1600, one per image part |
| glyph height in the image | 10 px in the source, unreadable after scaling | 10 px, unchanged |
| text rows the model can resolve | 0 | 8 |
| contrast in the text band | 11.2 | 29.7 |
Small images are not touched, and neither are images the provider would only trim a little: the plugin cuts a picture up only when the print it replaces would have been about seventy percent of the source's own size or less. An ordinary 1920x1080 screenshot is printed at 1708x960 — eleven percent narrower, which no one can see — so it stays whole and leaves the message's image budget to pictures that need it.
>=0.2.0-rc.1 <0.3.0 (declared in engines.dsh, and as peerDependencies on
@deepseek-ai/dsh-attachment / -attachment-local; verified against 0.2.0-rc.2). The range
covers the whole 0.2.x line, prereleases included, so a harness patch or rc bump does not
disable the plugin.sharp is bundled as a dependency. The plugin ships no prebuilt binaries of its own; pnpm
installs sharp's platform package for your machine as usual.dsh plugin --profile web add dsh-image-tiler@0.1.7
Two different versions matter here, and mixing them up is the usual mistake. Pin the plugin version
(@0.1.7): that is what keeps the install reproducible. Do not pin the harness: this package
accepts any harness 0.2.x, prereleases included, so a harness patch or rc bump does not disable it.
A harness the plugin cannot work with is refused before pnpm runs —
dsh: installation rejected: Plugin <name>@<version> is incompatible with dsh <version> — and
dsh plugin allow-version exists to override that by hand if you are testing an unreleased harness.
Add the dependency and the bundle to ~/.dsh/profiles/desktop/package.json, then install and
restart the app from the tray:
{
"dependencies": { "dsh-image-tiler": "0.1.7" },
"dsh": { "profile": { "bundles": ["...", "dsh-image-tiler"] } }
}
Send an image the provider would have printed at about two thirds of its size — a 2560x1440 screenshot, a 4K capture, an 8000x8000 page — and watch the host log:
image-tiler: 8000x8000 shown as 20 tiles of up to 2000x1600 (20.4x detail, 54% covered)
The number of image attachments in the message is what changed: one upload becomes N parts.
dsh plugin --profile web add dsh-image-tiler@0.1.7
dsh plugin --profile web remove dsh-image-tiler
The plugin adds no settings of its own. It is the store, so the only block it reads is the one the
official attachment-local row already accepts:
- id: attachment-local
disabled: true # the plugin serves `attachments` in its place
- insert:
- id: image-tiler
name: dsh-image-tiler
Every key of the official attachment-local row still applies, unchanged: dshHome,
maxImageBytes, maxImagesPerMessage, maxMessageImageBytes, maxImagePixels,
maxImageDimension, normalizedImageMaxPixels, normalizedImageMaxDimension,
normalizedImageMaxBytes, imageCompressionConcurrency.
Two of them decide what the tiler can do:
| key | default | effect on tiling |
|---|---|---|
maxImagesPerMessage | 20 | The tiling budget, shared evenly by the images in one message. A single 8000x8000 upload is spent as 20 tiles of 2000x1600 (20.4x the detail of the single downscaled image); three uploads in one message get 6 tiles each (about 15.5x) so no image is left behind at the provider's print. Lowering it makes each piece larger and less readable, because a piece bigger than normalizedImageMaxPixels is downscaled after the crop. |
normalizedImageMaxPixels | 4194304 | The ceiling on one published image. The planner is told this number and keeps every piece under it, so no piece is downscaled after being cut. |
maxImageBytes | 20971520 | The ceiling on one stored image. A tile bigger than this would make the store refuse the whole message, so the tiler measures every tile and sends the source whole if one of them is over. In practice a piece lands far below it: PNG is lossless, so even photographic tiles stay in single-digit megabytes. |
The row also has to be reachable: if the profile already raises the global image ceiling for huge uploads, keep those values. The plugin does not change them, and a source the store refuses never reaches the tiler.
prepare, install, or postinstall scripts; the package contains JavaScript, YAML, and text.sharp runs in-process through its native
binding.apply(ctx, config) mounts TilingAttachmentStore under the attachments service name, so it
replaces the official attachment-local store while extending it. Storage, admission limits,
normalization, and reads all stay the base implementation's.admitPromptContent(content) is the single call the session controller makes before it appends a
user message. The override plans a tile layout for each image part, crops the source with
sharp, and returns the tiles as image parts.lib/geometry.js, which transcribes the provider's own image projection
(patch/downsample grid, 1024-token budget) and picks the grid whose regions land closest to 1:1
with what the provider would otherwise have sent. A layout has to buy back at least twice the
detail per source pixel before it is worth the image parts it spends, so a source the provider
would print at nearly its own size yields exactly one tile.node test/geometry.test.mjs # tile geometry invariants, no test runner needed
node test/admission.test.mjs # prompt admission: budgets, fallbacks, part order
node test/_cap-table.mjs # layout table for each tile cap
node test/feasibility.mjs # legibility measurements on a generated fixture
test/ is not part of the published package.
normalizedImageMaxPixels (4194304, i.e. about 2048x2048) is downscaled after the crop, which is
why the planner fills the whole maxImagesPerMessage budget (20 by default) instead of using a
few big pieces.maxImagesPerMessage, so a lower value gives worse tiles. With the
budget lowered to 6, an 8000x8000 source becomes five 4579x915 strips: complete and clear, but not
the shape a reader would pick. The default of 20 produces the squarest layout.0.2.0-rc.2 on Windows. Other platforms are untested.MIT — see LICENSE.