Skip to content

Releases: 969246694/dsh-transparent

dsh-transparent 1.4.0 — settings panel renders

Choose a tag to compare

@969246694 969246694 released this 28 Sep 03:46

dsh-transparent 1.4.0 — the settings panel actually renders

What was wrong

1.3.3 registered a Config schema and nothing appeared. The schema was
accepted — the host reports status: "schema" — but a schema renders nothing
by itself
.

The Plugins page draws a bundle's configuration only for a bundle that
registered one, and registration is a browser-half job:

ctx.inject(['slots'], (child) => child.slots.register(
  { name: 'plugins.bundle.config', key: 'dsh-transparent' }, Panel))

The page then renders that section between the bundle's description and its
rows
— where you pointed, and where a reader looks for it.

How it reads and writes

Values come from ctx.remote.settings, the host's own configuration API:

  • describe() → every namespace with its live value and revision
  • mutate(ns, ops, revision) → path-addressed edits

The revision read is passed back, so the write is a compare-and-set — a
concurrent edit is reported rather than silently overwritten.

Why the slot service is optional

It is requested with ctx.inject([...]), not declared in the plugin's inject
list. A hard dependency would stop the whole plugin from applying if the
service were ever absent — and applying is what makes the window translucent.
The panel is optional; the window is not. Every failure path leaves the
plugin running.

Three attempts

problem
1.3.0 hand-written schema missing ~standard → plugin failed to start
1.3.2 schema could not resolve → no form
1.4.0 —

The first two were about making the host half describe the options. Neither
was about the half that draws them.

Restart the client to load it.

dsh-transparent 1.3.3 — settings schema resolves

Choose a tag to compare

@969246694 969246694 released this 28 Sep 03:02

dsh-transparent 1.3.3 — the settings schema now actually resolves

1.3.2 guessed that the application's module-resolution rewrite might let a
profile plugin import @deepseek-ai/schemastery. The restart answered it:

--- activated v1.3.2 (options: 0)

The rewrite covers a plugin's entry specifier — that is how bundled plugins
load by package name — but it does not extend to the imports inside a profile
plugin
. The guarded import did its job: the plugin ran, and simply had no
settings form.

The fix

schemastery and its one runtime dependency are now vendored under
lib/vendor/:

file from version licence
schemastery.mjs @deepseek-ai/schemastery 3.18.4 MIT
cosmokit.mjs @deepseek-ai/cosmokit 1.8.5 MIT

One import path rewritten. Both licences copied alongside, and NOTICE.md records
the provenance, the versions and the exact edit. The plugin stays
dependency-free — the same reason the stylesheet and the sprites ship inline.

Package size 55.8 kB, 17 files.

Verified locally, not assumed

Config exists                        true
Config['~standard'].validate         function    <- its absence broke 1.3.0
volatile fields resolve to           { get() }   <- why readValue exists
defaults applied                     alpha 215

verify.mjs asserts all four, plus that the vendored files are actually present
in the package — so a missing lib/vendor/ fails in CI instead of at load.
34 checks, all passing.

After updating

Restart the client. The settings UI should then show four controls: alpha,
darkTheme, updateCheck, updateSource.

If it does not, the log line says which case you are in:

--- activated v1.3.3 (options: 4)   settings form available
--- activated v1.3.3 (options: 0)   plugin fine, no form

dsh-transparent 1.3.2 — fixes the startup failure in 1.3.0

Choose a tag to compare

@969246694 969246694 released this 28 Sep 02:51

dsh-transparent 1.3.2 — fixes the startup failure in 1.3.0

If you installed 1.3.0 or 1.3.1, the plugin would not start. The application
reported it as 启动失败. Sorry — that was my fault, and the cause is specific.

What went wrong

1.3.0 declared a hand-written Config schema. The settings service reads such a
schema for three things — meta.volatile, ype/dict, and oJSON() — and
every one of them was verified against a genuine schemastery schema. All passed.

Loading uses one more thing that reading the settings service could never
reveal:

\\js
function resolveConfig(runtime, config) {
if (!runtime.Config) return config
const result = runtime.Config['~standard'].validate(config) // Standard Schema
...
}
\\

Config is validated through the Standard Schema interface. Schemastery
provides it; a hand-written object does not. Missing it throws during load and
takes the whole plugin down.

I verified the consumer I happened to be reading, and not the loader.

The fix

Stop imitating the schema, use the real one:

  • \Config\ is now \z.object({...})\ from @deepseek-ai/schemastery.
  • The import is guarded. It is the only part of the host half that depends on
    how the application resolves modules for a profile plugin. If it ever stops
    resolving, the plugin still runs and simply has no settings form — a
    degradation instead of an outage.

If you are on 1.3.0 / 1.3.1

Update to 1.3.2 and restart the client. Re-enabling the plugin is not enough:
the host half is imported once at startup and toggling reuses the cached module.

Verification

\�erify.mjs\ now checks for ~standard.validate\ on any exported Config —
that is the difference between a working plugin and a broken one — and checks
that the import stays guarded. In plain Node the import does not resolve, so the
check reports the degradation rather than failing on it, which also proves the
fallback path works.

dsh-transparent 1.3.1

Choose a tag to compare

@969246694 969246694 released this 28 Sep 02:39

dsh-transparent 1.3.1

Every activation now says which build is running

\
--- activated v1.3.1 (options: 4)
\\

Written to %TEMP%\dsh-transparent.log\ on every activation, together with the
existing lines. The option count is not decoration — it is what differs between
a build with no Config schema and one with it, which was previously invisible
from outside.

Why this exists: two questions that could not be answered

Is the running plugin the code on disk? Does the settings UI need a restart?

Testing settled both. The host half is imported once at startup, and
re-enabling the plugin re-runs �pply() from the cached module — ESM modules
are cached by URL, so toggling off and on does not pick up an edit. Verified
by adding a line only the new code prints, clearing the log, toggling the plugin,
and watching the two old lines appear while the new one did not.

What this means in practice

changed takes effect
\lib/client.js\ — the interface, glass, refraction refresh the page (\Ctrl+Shift+R)
\lib/index.js\ — window alpha, theme, update check, config schema restart the client

A page refresh only re-fetches the browser half through the /plugins\ route.
The host half runs in a different process and the refresh cannot reach it — which
is why the settings UI added in 1.3.0 needs a restart.

dsh-transparent 1.3.0

Choose a tag to compare

@969246694 969246694 released this 28 Sep 02:21

dsh-transparent 1.3.0

The options are now in the settings UI

alpha, darkTheme, updateCheck and updateSource appear in the
application's own settings UI with real controls, instead of only being editable
by hand in YAML.

Why the schema is hand-written

Declaring a Config schema normally means z.object({...}) from
@deepseek-ai/schemastery. A plugin installed under
<profile>/node_modules cannot import that package — it ships inside the
application bundle. Measured, not assumed:

import.meta.resolve('@deepseek-ai/schemastery')  ->  ERR_MODULE_NOT_FOUND

So the schema is a plain object shaped the way the settings service reads one.
That shape came from the service's own source, not from guesswork: it reads
exactly three things, and it is duck-typed throughout — no instanceof.

read what it needs
isVolatilePath schema.meta.volatile, schema.dict[key]
projectForm schema.type, schema.dict
plainSchema new z(schema.toJSON())

Every one of those was replicated against a genuine z.object(...volatile())
schema taken out of the application bundle, and produces identical results.

Two mistakes that testing caught

  • dict holds objects, refs hold uids. The live schema's dict must
    contain child schema objects, because isVolatilePath walks into them;
    only the serialised refs map uses uid strings. A first draft used uid strings
    in both, which would have made every option read as non-volatile — an empty
    form, and nothing logged anywhere.
  • Every node needs toJSON(), not just the root: volatileForm calls
    plainSchema on each volatile node individually.

And one that would have broken the plugin outright

Marking a field volatile changes how it is delivered. A volatile field
arrives at apply() as an accessor object with a get() — not as the value:

config.alpha        // { get() }   <- volatile
config.alpha.get()  // 200
config.plain        // 200        <- ordinary

Reading cfg.alpha directly would have silently reverted every option to its
default, with no error attached to it. All four options now go through an
unwrapping helper that accepts either shape — so the plugin also keeps working
if a future runtime stops resolving the schema at all.

Verification

verify.mjs grew a section for this: 33 checks, all passing. It tests the
schema against the service's own logic and does not import schemastery,
which is not installable from a plugin and would make CI fail for the wrong
reason.

dsh-transparent 1.2.1

Choose a tag to compare

@969246694 969246694 released this 27 Sep 16:36

dsh-transparent 1.2.1

autoUpdate is gone

1.2.0 added both an update check and the ability to fetch the update. The second
half was removed, because it rewrote your profile to buy nothing: a new version
only starts running after a restart either way, and a failure part way through
leaves the plugin removed.

Updating is already one step you have — reinstall through the plugin manager, or
install_bundle dsh-transparent, which re-runs pnpm and takes the newest version
in range.

What stayed, and why

The plugin manager has no update affordance and no version check — there is
no outdated, upgrade or checkForUpdate anywhere in it. So nothing in the
application will ever tell you a newer version exists. Without the check you stay
on whatever you installed, which is a real gap rather than a hypothetical one.

It is one small request against the registry your profile is configured for
(including the China mirror), its failure is silent, and it now says plainly what
to do:

[transparent] v1.2.1 is available (running 1.2.0) — reinstall the plugin to update

Config

config:
  alpha: 215          # whole-window opacity
  darkTheme: true     # pin the interface to the dark theme
  updateCheck: true   # say so when a newer version exists
  updateSource: ""    # override where the version is read from

If you are on 1.2.0

Nothing breaks. autoUpdate is simply ignored from 1.2.1 on, and it defaulted to
off anyway.

Still true

The liquid-glass composer is implemented and its refraction is real, but it stays
barely visible: backdrop-filter only sees content inside the page, and nothing
scrolls behind the composer. See the README.

dsh-transparent 1.2.0

Choose a tag to compare

@969246694 969246694 released this 27 Sep 16:32

Superseded by 1.2.1. The autoUpdate option described below was removed: it rewrote the profile to buy nothing, since a restart was needed either way. Nothing else changed.

dsh-transparent 1.2.0

New: built-in update check

The plugin manager has no update action — an installed bundle cannot be
re-installed over itself through the GUI, and there is no outdated, upgrade
or checkForUpdate code path anywhere in it. So the host half now checks for
itself, through the pluginManager service the manager already exposes:

[transparent] v1.2.0 is available (running 1.1.0) — set autoUpdate: true to fetch it automatically
  • registries() is read first, so the check goes to whichever registry your
    profile is configured for — including the China mirror, rather than being sent
    to a host you cannot reach.
  • installBundle() is the update itself, used only when autoUpdate is on.
  • The check costs one small request. A failure is silent: an offline machine, a
    private registry or a blocked route is not worth a warning.

autoUpdate is off by default, on purpose

Turning it on makes the plugin rewrite your profile. A failure part way
through leaves the plugin removed.
That risk belongs to the person who chose
it, not to the default.

Either way, a restart is what actually runs the new version.

A working copy is never "updated"

The check is skipped unless the package sits under node_modules. The obvious
test — "is my own directory a symlink?" — does not work, because Node resolves
symlinks and a linked install reports the real path of the checkout. The path
itself is the reliable signal. Without this, running from a checkout could
replace your source tree with a published tarball.

Config

config:
  alpha: 215          # whole-window opacity
  darkTheme: true     # pin the interface to the dark theme
  updateCheck: true   # say so when a newer version exists
  autoUpdate: false   # fetch it instead of just saying so
  updateSource: ""    # override where the version is read from

Also fixed

A helper that exits non-zero carries no reason, so the failure line read
FAIL undefined and threw away the exit code and stderr — the only diagnostics
that existed. Found while testing this release.

Still true

The liquid-glass composer is implemented and its refraction is real, but it stays
barely visible: backdrop-filter only sees content inside the page, and nothing
scrolls behind the composer. See the README.

dsh-transparent 1.1.0

Choose a tag to compare

@969246694 969246694 released this 27 Sep 15:44

dsh-transparent 1.1.0

New: the interface is pinned to the dark theme

Translucency over a light interface reads as grey rather than glass, so the
plugin now sets the theme to dark on activation.

It goes through the host settings service — writing ui-theme.preference —
rather than setting an attribute on <body>. The theme is a durable user
preference the host owns; an attribute would be re-imposed by the next render,
and the change would not survive a restart or appear in the settings UI.

Two details worth knowing:

  • It reads before writing. If the preference is already dark nothing is
    written, so startup does not rewrite the settings document every time.
  • The write is a compare-and-set. The revision read from describe() is
    passed back to update(), so a change you made in the settings UI in the
    meantime is not silently overwritten.

Turn it off with darkTheme: false. Uninstalling does not restore the previous
preference
— if you want your own choice respected, set that flag.

Config

config:
  alpha: 215          # whole-window opacity
  darkTheme: true     # pin the interface to the dark theme

Still true from 1.0.x

The liquid-glass composer is implemented and its refraction is real, but it stays
barely visible: backdrop-filter only sees content inside the page, and nothing
scrolls behind the composer. See the README.

dsh-transparent 1.0.1

Choose a tag to compare

@969246694 969246694 released this 27 Sep 14:25

Docs-only patch. The npm page shows the README from inside the tarball, and
1.0.0 shipped before the install section described the mirror route.

Install from npm (no GitHub access needed) — set the install source to
中国大陆镜像源 (registry.npmmirror.com) and use the package name:

dsh-transparent

Everything else is unchanged from 1.0.0. See that release for the full notes,
including the honest limitations: a GitHub URL is fetched by git and bypasses
npm, so the mirror only helps the npm route.

dsh-transparent 1.0.0

Choose a tag to compare

@969246694 969246694 released this 27 Sep 13:16

dsh-transparent 1.0.0

Unofficial plugin for the DeepSeek Harness desktop client. Windows only.

What it does

Effect Status
Window becomes translucent, wallpaper shows through Works
The app's opaque background is cleared Works
The composer refracts its backdrop instead of just blurring it Implemented, but barely visible in practice

Please read before installing: what you will actually see is a translucent
window. The liquid-glass implementation is real — it generates a normal map of
the composer's rounded rectangle at runtime and feeds it to an SVG
feDisplacementMap through backdrop-filter: url() — but refraction needs
content passing behind it, and nothing scrolls behind the composer because the
message list ends above it. Details in the README.

Install

plugin_manager install_bundle  https://github.com/969246694/dsh-transparent

Pin this release instead of tracking main:

plugin_manager install_bundle  https://github.com/969246694/dsh-transparent#v1.0.0

Then restart the client. lib/client.js is committed, so no build step is needed
after installing.

Configuration

cordis.patch.yml:

config:
  alpha: 215   # 255 = opaque and crisp, 215 = default, 150 = wallpaper obvious

Design tokens live in dsh-transparent.css — edit, then run node build.mjs;
the browser half hot-reloads.

Architecture, in one paragraph

Styles are applied with CSS selectors that match by structure, not by JavaScript
marking elements. Marking cannot avoid flicker: the app replaces its own elements
when it re-renders, the replacement carries no marker, the rule stops applying,
and the plugin re-applies — and that gap is visible. So the browser half does
exactly two things, both once: resolve how deeply the composer sits above the
editable element and append one :has() rule for that depth, and build the lens
map. There is no MutationObserver, no scheduler and no re-marking. If the
composer is replaced a thousand times, the browser matches it a thousand times
and none of the plugin's code runs.

Quality gates

  • Zero dependencies. build.mjs and verify.mjs use only Node's standard library.
  • verify.mjs covers the client-half contract, packaging, the throwing-global
    traps, YAML validity, ASCII purity of the PowerShell helper, and build drift.
  • CI runs both jobs on Windows; both are green.

中文

面向 DSH 桌面客户端的非官方插件,仅支持 Windows。

效果 状态
窗口变半透明,桌面壁纸透出来 有效
应用的不透明背景被清掉 有效
输入框折射背景,而不只是模糊 已实现,但实际几乎看不出

安装前请注意:你实际能看到的效果就是「窗口变半透明」。液态玻璃的实现是真实的——运行时生成输入框圆角矩形的法线贴图,通过 backdrop-filter: url() 交给 SVG feDisplacementMap——但折射需要有内容从后面经过,而消息列表的底边在输入框上方,没有任何东西会滚到它后面。详见 README。

安装:

plugin_manager install_bundle  https://github.com/969246694/dsh-transparent#v1.0.0

已知限制:壁纸本身无法被折射(backdrop-filter 只看得见页面内部);聊天文字不会经过输入框后面(要改变这一点的三次布局尝试全部回退了)。

详见 README.zh-CN.md。