dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
DSH Web UI 的一键刷新:先清掉可重建的缓存(Cache Storage、Service Worker、/plugins/** 的 bundle 字节)再重载页面,热注入的客户端插件立刻生效,无需重启应用。
One-click 刷新页面 button for the DSH Web UI: a small translucent ⟳ pinned to the frame's bottom-right corner, available in every view.
Injecting or editing a client plugin updates the Host's cordis tree live, but the browser half that is already loaded keeps running the old code — so the only reliable way to see the change used to be quitting and reopening the whole app. This button reboots the Web page against the freshly published module graph, so a hot-injected plugin appears immediately.
| Half | Entry point | Work |
|---|---|---|
| Host | lib/index.js | nothing (keeps the package a valid bundle row) |
| Client | lib/client.js | shell.overlay / id quick-reload / order 100 / label from locale |
shell.overlay is root scope and kind list with replaceRisk: none: the
button lives in the frame-wide floating layer
(.BynINW_overlayLayer{z-index:20;pointer-events:none;position:absolute;inset:0}),
so it is global (mounted with the layout frame, not with a Session), additive
(a fresh id can never replace or displace anyone), and it shares a row with
nobody — every occupant positions itself.
The layer is click-through, so this entry opts back in with pointer-events:auto.
| Seat | Why not |
|---|---|
sidebar.footer.action | Renders into ._2H3hWW_footerActions{display:flex} — one flex row. dsh-context's context-overview entry there is width:calc(100% + 4px) + margin:0 -2px, i.e. it wants the whole row; a second entry overlaps it (this was the original bug). Forcing my item to flex:0 0 36px would stop the overlap but squeeze the other plugin's label instead. |
conversation.session.header.actions | Session-scoped: it does not exist without an open Session. Also a shared, width-constrained action row. |
sidebar.panellist | Its ids address a main panel — clicking switches the main column. Wrong semantics for an action. |
conversation.header.corner, shell.leading, sidebar.* singletons | Single slots: registering shadows the shipped UI (replaceRisk: shadows-shipped-ui). |
Bottom-right, inset 18px — the frame's floating-pill convention. That corner is
free of persistent plugin UI: the only foreign shell.overlay entry is
dsh-market-toast (a transient install toast), toasts otherwise anchor to the
top through the primitives' overlayTopMargin() helper
(max(min, --dsh-frame-top-clearance + 20)), and dshmarket's own bottom-right
pill (.nUhMVa_top) is position:absolute inside the market panel
(.nUhMVa_root{position:relative}), not in this frame layer.
The button is 32px and translucent (.68 opacity) until hover/focus, so even
when something transient passes underneath, nothing is really hidden.
A page reload alone can keep serving stale JavaScript: the Host advertises every
bundle under /plugins/** with Cache-Control: public, max-age=31536000, immutable. That is fine while a bundle's rev moves with it — but the rev is
derived from the artifact's file metadata (mtimeMs / ctime / size), not
from its bytes, so two different builds can share one rev and the year-long cache
then wins. So the click purges the rebuildable caches before reloading:
dsh-remote-shell-v1, which that plugin re-prefetches on its next visit).update(), so whatever the SW
serves comes from the Host again.GET /plugins/events, the SSE frame the Host already broadcasts), and every
/plugins/** URL it advertises — plus every one this page already loaded,
taken from the performance timeline — is re-fetched with cache:'reload'.
Bounded: at most 160 URLs, a 1.2s timeout per request, a 2.2s overall budget,
so the reload never waits longer than that.localStorage, sessionStorage, cookies, the profile on disk, your Sessions and
the open conversation are not touched. That is where real state lives: per-
Session drafts, pane widths, and the remote-access device credential
(dsh-remote-device) — wiping those would lose work or break remote pairing.
Nothing in this path reaches the Host process or the agent runtime: no app
restart, no /dsh-market/restart, no profile write. A running turn, a background
job or a terminal session keeps running while the page reloads, and the page
reconnects to it afterwards. For a bare reload with no cache work, Shift-click
the button.
The npm package name is dsh-quick-reload-lyjs (the plain dsh-quick-reload
is taken on npm); the repository and the plugin id keep the short name. From the
release tarball — prebuilt, nothing to compile (with the desktop app fully quit):
dsh plugin --profile desktop add https://github.com/lyjsyyds/dsh-quick-reload/releases/download/v0.1.1/dsh-quick-reload-lyjs-0.1.1.tgz
Or through the GUI — 设置 → 插件 → 安装 — with that same URL as the spec.
From a local checkout (the development form; the CLI writes both dependencies
and dsh.profile.bundles itself):
dsh plugin --profile desktop add link:<absolute path to this directory>
Uninstall the bundle in 设置 → 插件 (or dsh plugin --profile desktop remove dsh-quick-reload-lyjs) and drop it from dsh.profile.bundles; the button disappears
on the next start.
rev alone already defeats the
immutable cache; the revalidation pass exists for the rev-collision case (same
metadata, different bytes).npm test # or: node tests/quick-reload.smoke.mjs
tests/quick-reload.smoke.mjs loads the bundle the way the Host's module loader
does (window.__ModuleLoader__.load), applies the plugin to a stub context,
renders the overlay entry with a small React stand-in that really tracks state,
and drives the whole click path against stubbed browser globals — Cache Storage,
service workers, the /plugins/events SSE frame, the performance timeline and
fetch. 19 checks, no dependencies, no network: it also pins the bounds (at most
160 URLs, one revalidation per URL) and proves a wedged origin cannot hold the
reload. A few checks are timing assertions, so the file takes about 3 seconds.
MIT © 2026 LYJS. Source: https://github.com/lyjsyyds/dsh-quick-reload