dsh-any-background 0.3.9 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -24
- package/README.zh.md +17 -24
- package/lib/client.js +349 -95
- package/lib/client.js.map +1 -1
- package/lib/index.js +329 -73
- package/package.json +11 -10
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<a href="https://www.npmjs.com/package/dsh-any-background"><img alt="npm version" src="https://img.shields.io/npm/v/dsh-any-background?color=4d6bfe"></a>
|
|
5
5
|
<a href="https://www.npmjs.com/package/dsh-any-background"><img alt="npm monthly downloads" src="https://img.shields.io/npm/dm/dsh-any-background?color=4d6bfe"></a>
|
|
6
6
|
<a href="https://github.com/Tkingxiao/dsh-any-background/blob/main/LICENSE"><img alt="License: MIT" src="https://img.shields.io/npm/l/dsh-any-background?color=4d6bfe"></a>
|
|
7
|
-
<a href="https://www.npmjs.com/package/@deepseek-ai/dsh?activeTab=versions"><img alt="Supported DSH versions: 0.2.0-rc.1 ~ 0.2.1-rc.
|
|
7
|
+
<a href="https://www.npmjs.com/package/@deepseek-ai/dsh?activeTab=versions"><img alt="Supported DSH versions: 0.2.0-rc.1 ~ 0.2.1-rc.3 and above" src="https://img.shields.io/badge/DSH-0.2.0--rc.1%20~%200.2.1--rc.3%2B-4d6bfe" /></a>
|
|
8
8
|
<a href="https://github.com/topics/dsh-better-sidebar"><img alt="Plugin ecosystem: GitHub topic dsh-better-sidebar" src="https://img.shields.io/badge/plugin%20ecosystem-topic%20dsh--better--sidebar-4d6bfe" /></a><br /><br />
|
|
9
9
|
<a href="https://github.com/Tkingxiao/dsh-any-background"><img src="https://img.shields.io/github/stars/Tkingxiao/dsh-any-background?style=social" alt="GitHub stars"></a>
|
|
10
10
|
<a href="https://dsh.directory/plugins/tkingxiao/dsh-any-background"><img src="https://dsh.directory/badges/listed.svg" alt="dsh.directory listed"></a>
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
English | [中文](README.zh.md)
|
|
14
14
|
|
|
15
|
-
A **DeepSeek Harness** appearance plugin: custom theme color, background wallpaper (image / video / algorithmically generated), and fine-grained per-surface opacity & blur controls. Compatible with **DSH `0.2.0-rc.1` through `0.2.1-rc.
|
|
15
|
+
A **DeepSeek Harness** appearance plugin: custom theme color, background wallpaper (image / video / algorithmically generated), and fine-grained per-surface opacity & blur controls. Compatible with **DSH `0.2.0-rc.1` through `0.2.1-rc.3` — and every host release above it** (official-Sidebar UI such as the "Theme" card enables itself where the host exposes the Sidebar registry extension point, and is skipped silently where it does not).
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
@@ -82,9 +82,9 @@ controls (`z-index:-1`) and carries its own ink (`--dsh-any-caption-ink`), so th
|
|
|
82
82
|
应用 / 编辑 menubar stay readable even when a low sidebar opacity makes the strip light — and 「标题条带文字」 says who wins when the strip's brightness and the theme's direction disagree (自动 derives it from the strip, 跟随主题 leaves the host's own label ink alone, 始终白色 / 始终黑色 pins it; see issue #30). The anchor is expressed in CSS (the row is the `conversation.session.header` slot's direct parent) instead of guessing a hashed class or tagging a host element at runtime; before `0.1.7-alpha.1` the host mounts that slot bare with no such element, so there the rule matches nothing and the part is simply inert. On the Windows desktop shell the same slider pair also drives the window caption band — except for the segment that sits above the left sidebar, which belongs to that sidebar's own pair (see **Left sidebar + the caption band above it**) — and the native minimize/maximize/close block is handed `transparent` so Windows paints no background of its own, leaving the band itself showing through. A blank session marks the row `headerBlank` (a placeholder left with only its padding) and the plugin skips claiming it.
|
|
83
83
|
- **Sidebar "Theme" page (dual mode)** — The same five pages (Color / Interface / Font / Background / Profiles) register into two surfaces: without dsh-better-sidebar, a "Theme" card is contributed to the **official right Sidebar's guide page** through its public extension points (`sidebarRightTabs` + the `sidebar.right.pane.tab` keyed seat); with dsh-better-sidebar installed, the page registers in that plugin's sidebar instead and the official guide card withdraws itself, so the two never duplicate. Settings panel, official sidebar and better-sidebar all share one page implementation and one state store — a change in any of them shows up everywhere. The shell adapts to the panel width, and a narrow panel tightens padding and falls back to a single column. On a host without the right Sidebar the registration silently never happens.
|
|
84
84
|
- **Conversation View Cards** — The message list is wrapped in a translucent card automatically, and the trajectory page gets whole-page opacity & blur controls, letting the wallpaper shine through the content.
|
|
85
|
-
- **Theme Export / Import** — One-click export to a self-contained `dsh-any-theme.json` (config + wallpaper, video embedded as a data URL) and import to restore it anywhere.
|
|
86
|
-
- **Appearance Presets & Profiles** — Six one-click presets (Default / Frosted glass / Minimal / Midnight / Cyber / Warm daylight) plus named profiles: save the current look and re-apply it anytime
|
|
87
|
-
- **Wallpaper Rotation** — Add images to a rotation pool (thumbnail picker included) and let the wallpaper change by shuffle or order on every refresh, daily, or weekly. Advancing copies the chosen image into the active wallpaper slot, so export/import and color extraction keep working unchanged.
|
|
85
|
+
- **Theme Export / Import** — One-click export to a self-contained `dsh-any-theme.json` (config + wallpaper, video embedded as a data URL, and — from v0.4.0 — the whole rotation pool's image bytes) and import to restore it anywhere.
|
|
86
|
+
- **Appearance Presets & Profiles** — Six one-click presets (Default / Frosted glass / Minimal / Midnight / Cyber / Warm daylight) plus named profiles: save the current look and re-apply it anytime; profiles can be renamed and duplicated, and a two-step confirm guards deletion.
|
|
87
|
+
- **Wallpaper Rotation** — Add images to a rotation pool (thumbnail picker included) and let the wallpaper change by shuffle or order on every refresh, hourly, daily, or weekly — the cadence advances while the page stays open, not only on reload. Advancing copies the chosen image into the active wallpaper slot, so export/import and color extraction keep working unchanged.
|
|
88
88
|
- **Day/Night Auto Switch** — Assign a day profile and a night profile; the plugin switches automatically at fixed clock times or by following the OS dark mode.
|
|
89
89
|
- **Custom Font** — Upload a ttf / otf / woff / woff2 file (up to 100 MB) and apply it to the whole interface through `@font-face`; toggle it off or remove it at any time. Fonts stream as raw bytes and persist in the plugin data dir; code blocks keep their monospace stack.
|
|
90
90
|
- **Per-part Text Outline** — The same surface groups as the interface page (now eleven, including the header popovers and the header bar), each with its own `-webkit-text-stroke`: width 0–4 px (0 = off) and a color of auto-contrast / gray / black / white / accent / custom. Code blocks, inline `code`, icons and the host's `background-clip: text` shimmer chrome (the "深度求索中" turn-status line and the turn-process rows) are exempted automatically, so multi-color syntax never smears and gradient text is never flattened into a stroke-coloured blob.
|
|
@@ -95,26 +95,19 @@ controls (`z-index:-1`) and carries its own ink (`--dsh-any-caption-ink`), so th
|
|
|
95
95
|
|
|
96
96
|
## Changelog (latest two releases)
|
|
97
97
|
|
|
98
|
-
### v0.
|
|
98
|
+
### v0.4.1 (fix: the "Every refresh" rotation cadence flipped the wallpaper every 30 seconds)
|
|
99
99
|
|
|
100
|
-
- **
|
|
101
|
-
- **
|
|
102
|
-
- **The 「标题栏」 row's frost stopped eating its own children's blur.** `backdrop-filter` on the row made the row a BACKDROP ROOT as well as a stacking context, so an in-place header popover could only ever blur what the row itself paints — a flat tint — and the header blur slider read dead on exactly the panel that hangs off the row. The frost now rides a `::before` underlay behind an `isolation:isolate` (a stacking context that is NOT a backdrop root), which is the recipe this plugin already uses for the frame, the composer capsule and every part column; the row keeps its compensating `z-index:10`, and the underlay's `position:relative` is written inline only while a blur exists and only when the host left the row static. Measured in the real `dsh web` app (the plugin linked from this working tree, driven through the actual 「顶栏选项」 sliders on the 界面 page): the in-place `…` menu and the portaled open-in-app menu both report `rgba(243,247,252,1)` at opacity 100 / blur 4 and `rgba(243,247,252,0.08)` at opacity 8 / blur 44. Measured again in a browser harness running the plugin's real client code over the host's own built CSS: the row reports `backdrop-filter: none`, its `::before` reports the slider's blur, and every header popup shape — portaled dense `MenuSurface`, in-place dense, in-place non-dense, the role-less job-list surface, the lineage tree — takes the 顶栏 pair's fill and frost instead of the card's.
|
|
103
|
-
- **The interface-opacity sliders work on a fresh install now.** `readHostOpacityTokens()` sampled `getComputedStyle(:root)`, but `design-platform.css` declares this whole token set inside a `body { … }` block and a custom property is inherited downward only — so with no picked theme color every read came back empty, `applyCustomTokensNow` took its `surfaces === null` exit, and the 侧栏 / 卡片 / 输入框 / 顶栏选项 alphas were never emitted at all. The sample now comes off `body`, cached per host generation (its dark-mode marker plus the platform attribute), and the plugin's own `body{}` token sheet is switched off while reading — otherwise the second sample returns the plugin's own substituted literal and the surfaces freeze at whatever color they first had instead of following the host.
|
|
104
|
-
- **A 0 on a menu blur slider now means no frost.** 0.3.7 introduced a `<html>` class gate (`dab-card-frost` / `dab-header-frost`) so a slider parked at 0 would not delete the host's own `--dsw-menu-backdrop-filter: blur(40px) saturate(150%)` — an element's own declaration always beats what it inherits, so the un-gated `var(--x, none)` really did clear it. The gate turned out to be the wrong trade: it made the slider's LOW end its HIGH end (0 → the host's 40 px, 1 → 1 px), so 「把模糊度关掉」 was unreachable and the control read as dead at exactly the position the user was testing. The frost token is now asserted at every value with the `none` fallback winning at 0, and the gate machinery is gone. Nothing loses the host look by accident: every part default is 30 px, so a stock install is frosted and only an explicit drag to 0 clears it.
|
|
105
|
-
- **No release-table change.** The shapes above were re-checked against the installed `0.2.0-rc.1`, `0.2.1-alpha.3` and `0.2.1-rc.2` bundles: both utilities controls, `MenuSurface`'s `data-menu-material` + `.material` painter and the menu token set are identical across the three, so the `0.2.1` adapter's facts still hold.
|
|
100
|
+
- **What happened.** v0.4.0 taught the 30-second schedule tick to advance a due rotation so an `Hourly` cadence works while the page stays open. But the due-math says "always due" for the `Every refresh` cadence — that is how a page load earns a fresh pick (the server advances it inside the `read` RPC). Wired into the tick, it meant: rotation enabled + `Every refresh` selected → the wallpaper switched itself every 30 seconds, and again on every OS dark/light flip or enabling the day/night schedule (both also drive the tick).
|
|
101
|
+
- **The fix.** Whether a cadence participates in live ticking is now a shared-math question: `rotationAdvancesLive()` in `src/shared/rotation.ts` (inlined into both bundles, like the rest of the cadence math) says only the time-based cadences — hourly / daily / weekly — do. `Every refresh` keeps its exact original semantics: one fresh pick per page load, plus the manual next button. Boot-time fallback and the enable-toggle behavior are unchanged.
|
|
106
102
|
|
|
103
|
+
### v0.4.0 (persistence-integrity batch + rotation / export / profile features)
|
|
107
104
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
- **
|
|
111
|
-
- **
|
|
112
|
-
- **
|
|
113
|
-
- **
|
|
114
|
-
- **The band has two owners ON PURPOSE.** The segment above the left sidebar follows the 「侧栏」 pair (same material literal, same blur mirror); everything right of the sidebar width follows the 「标题栏」 pair. That is why moving the header-bar sliders only changes the right part (issue #29 item 3) — the boundary is `--dsh-windows-sidebar-width`, published by the host, so it follows a dragged or collapsed sidebar by itself.
|
|
115
|
-
- **The sidebar reads as ONE surface now, so its top strip and its body stop splitting.** `--dsw-specific-sidebar-fill` is a theme token the host ships OPAQUE, and three elements inside the sidebar's box each declare `background:var(--dsw-specific-sidebar-fill)` on top of one another: `AppFrame`'s `.sidebarCol` (the grid track), `SidebarRoot`'s `.root` (the shell inside it) and the workspace tree's `.fade` (a 24 px bottom gradient into that same fill). Over an opaque color that stacking is invisible; with the 「侧栏」 opacity slider it is not, and on the desktop it became a hard line — the caption strip above the sidebar is the plugin's own plate, painted with the same variable at the same alpha exactly once, so the strip sat at one density and the column under it at another (the reported desktop split). The token is now re-declared `transparent` for the column's descendants (`SIDEBAR_ONE_LAYER_RULE`), which leaves the outermost box as the single painter — the same owner the host already picked on macOS, where `SidebarRoot.module.css` clears its own `.root` fill for precisely this reason. Measured in a mock built from the installed bundles: before, column 0.22 over root 0.22 (≈0.39) against a 0.22 strip; after, one 0.22 everywhere. Bound by the CSS-Module class shape (`[class*="_sidebarCol"]`, identical in `0.2.0-rc.1` / `0.2.1-alpha.3` / `0.2.1-rc.2`), so a build that renames the track just keeps the untouched host look.
|
|
116
|
-
- **The plugin-install dialog's npm-registry panel keeps the host's opacity.** `<fieldset data-install-registry>` is `createPortal`ed to `document.body` as a `position:fixed; z-index:1100` panel and paints from `--dsw-alias-bg-layer-2` (itself and its toggle) and `--dsw-alias-bg-layer-3` (the expanded toggle, the typed-address input) — tokens the 「卡片」 slider owns globally, so the panel faded with the card slider while the host's own comment on that rule says the layer is opaque *"to keep the registry form controls readable over the dialog"*. It joins the exemption set `applyExemptDefaults` already pins for confirm dialogs and the session-row menu: the current theme color at alpha 1, no translucency, nothing else re-inked or re-tinted, and the pin spelled for the subtree as well because a custom property travels downward and the options live inside it.
|
|
117
|
-
- **The `0.2.1` line is one channel, and every checked build is named.** `0.2.1-alpha.3` moved the background-job list onto the shared `MenuSurface` — the surface root carries no `role`, and the fill plus the frost live on its `.material` child — so the runtime tagger now also claims body-level `[data-menu-material]` surfaces and the header selector set gained the matching arm; the older self-painting `<ul class*="_menu">` shape stays matched. Verified per tag from `alpha.3` through `rc.2`: no file the adapter speaks for moved (`design-platform.css`, `MenuSurface`, `MenuGroup`, `AppFrame`, `ConversationRoot`, `TaskMenu`, `preload-windows`, the five header slots). `SidebarRight.module.css` only gained `overflow:clip` on the panel wrapper (fixed floating children still escape), and the plugin-manager page was refactored with all three list anchors intact. The `0.2.1-alpha` and `0.2.1-rc` channels therefore collapse into one `0.2.1` channel covering `alpha.1`…`rc.2`, with a `SUPPORTED_RELEASES` row per build (each `exact`), `engines`/peers/`dsh.compatibility` naming them, and the folder renamed `v0-2-1`.
|
|
105
|
+
- **Theme exports now carry the whole rotation pool (v3).** The old export carried the config and the active wallpaper only: the pool's metadata (file names + thumbnails) travelled with the config while the image bytes stayed on the source machine, so after an import on another machine every activation failed with `file missing`. A v3 export embeds each pool image's bytes (fetched from the new `/dsh-any-background/rotation/file` route, whose name goes through the same anti-traversal fence the config sanitizer uses) as `pool`; the import runs a new `rotationRestore` RPC that rebuilds the pool wholesale — bytes land under their exported file names, unreferenced files are dropped, and the cursor clamps into the new range. Dead entries in v2 files clear on import instead of failing on every activation.
|
|
106
|
+
- **Rotation gains an hourly cadence — and advances while the page stays open.** Advancing used to happen only inside the `read` RPC on page load, so a long-lived window never changed wallpaper; the 30-second schedule tick now re-checks it too (pure math gate — a no-op when nothing is due). The due/pick math moved into a shared module inlined into both bundles at build time (`src/shared/rotation.ts`), so the two halves can never disagree on a day/week boundary again.
|
|
107
|
+
- **Profiles can be renamed and duplicated.** Each row gains an inline rename (Enter commits) and a one-click duplicate (fresh id, `(2)` name suffix, same 20-entry oldest-eviction limit).
|
|
108
|
+
- **Persistence correctness fixes — each matching a failure mode this repo has already hit.** `readConfig`'s failure path handed back a shallow copy whose nested arrays rotation add/remove mutated in place, leaving ghost pool items in the module-level defaults forever (now `structuredClone`); a failed config write left the mtime-keyed cache describing changes the disk never got (cache dropped before writing); the font slot's stray-file adoption mislabelled MIME by file name, serving woff2 bytes as ttf (now sniffed from real magic bytes and persisted); raw wallpaper uploads skipped byte validation (now the same magic-number fence as the URL download and the font upload); rotation file names could collide within one millisecond (random suffix added); manual activation did not persist the cursor, so a reload jumped back to the old image (server-side read-modify-write, the same semantics as the auto-advance).
|
|
109
|
+
- **Structure & performance.** The browser half's four body-level MutationObservers merged into a single bus (`src/client/dom-watch.ts`, auto-disconnecting on the last unsubscribe); the parts pass moved from rAF to microtask coalescing — the v0.3.9 lesson (a queued frame never runs while the window is not painting), so a view or takeover card mounted in the background is styled immediately; `applyWp`'s fan-out deduplicated (the produced / header-popover passes ran twice per apply); a failed config write retries once (a transient Windows EBUSY from an antivirus scan) and then warns explicitly instead of being swallowed; `read` probes its three slots concurrently; a first run writes the config once, not twice; startup sweeps crash-leftover tmp files, excess `.corrupt-*` archives (keeping the newest 3) and orphaned pool files (10-minute grace); rotation advance/activate uses `copyFile`, so multi-MB wallpapers no longer pass through the heap whole.
|
|
110
|
+
- **i18n cleanup.** Four dead keys removed and embedded Chinese quotes in the English copy replaced; zh/en at 197/197 key parity.
|
|
118
111
|
|
|
119
112
|
## Installation
|
|
120
113
|
|
|
@@ -158,8 +151,8 @@ pnpm dsh web
|
|
|
158
151
|
|
|
159
152
|
## Compatibility
|
|
160
153
|
|
|
161
|
-
- **[`dsh web`](https://github.com/deepseek-ai/deepseek-harness) `0.2.0-rc.1` ~ `0.2.1-
|
|
162
|
-
- **Isolated per-version adaptation**: the release is resolved on the Node half from the app manifest the process was composed from (`ctx.profileContext.installAnchor`, with the launcher's on-disk layout behind it — the client context exposes no version) and, once handed down through the `read` RPC, is routed only by the front-layer adapter — `src/host-compat/` detects and buckets channels, while `src/client/host-compat/versions/` holds **one folder per set of verified facts** (`v0-2-0-rc-1` / `v0-2-0-rc-2` / `v0-2-1` / `unknown`), each describing that version's panel mechanics, header slot keys, plugin-page shape and where a menu's paint lives. A folder is keyed to what its facts cover, not to the shape of the version number: `0.2.0-rc.1` and `0.2.0-rc.2` are one prerelease channel, but rc.2 renamed the token a sticky menu group heading paints from, so the line carries two folders and the newer one is named for the build that opened it. A newer build whose facts did NOT move joins the folder that held instead of opening one of its own — the whole `0.2.1` line (`alpha.1` … `rc.
|
|
154
|
+
- **[`dsh web`](https://github.com/deepseek-ai/deepseek-harness) `0.2.0-rc.1` ~ `0.2.1-rc.3`, and everything above it with it** — `engines.dsh`, the gated `@deepseek-ai/dsh-*` `peerDependencies` and `dsh.compatibility.dsh` name the builds this plugin was checked against one by one (`0.2.0-rc.1`, `0.2.0-rc.2`, `0.2.1-alpha.1` … `0.2.1-rc.3`) and close with an uncapped `>=0.2.0-rc.1`, so every later release loads. The uncapped umbrella is the deliberate choice: the host enforces exactly this peer list — a release it does not name is disabled before a single module is imported — so a range that runs out is a wall in front of a plugin that would have gone on working. An unseen release gets the closest verified adapter plus a log line saying how far from `exact` that is, and the worst a stale guess can cost is one slider no longer fading a surface the host has since moved; a version check that failed closed would have cost the whole plugin. `dsh.compatibility.dshReleases` records which builds were checked and how: every release in the list was checked by diffing its tag against the adapter folder that answers for it. That enforcement still means a `dsh web` restart when the plugin is installed or updated — the check runs while the profile is composed, not when the plugin is updated — but the uncapped umbrella is what spares a machine that has already moved to a newer host from needing a plugin release or a manual exact-version exemption (`dsh plugin allow-version`) first. The host release is resolved on the Node half at runtime, and features that depend on a specific host release channel (the right Sidebar's panel blur, the official Sidebar's "Theme" card) enable themselves only where the corresponding host structure exists; everything else behaves identically across the range. THE `0.1.5` / `0.1.6` / `0.1.7` ERA IS OUT OF THE SUPPORTED RANGE: those adapter folders asserted facts nothing re-checks any more, and the range above now starts at `0.2.0-rc.1`, so an older host is refused at composition rather than quietly styled with the wrong adapter.
|
|
155
|
+
- **Isolated per-version adaptation**: the release is resolved on the Node half from the app manifest the process was composed from (`ctx.profileContext.installAnchor`, with the launcher's on-disk layout behind it — the client context exposes no version) and, once handed down through the `read` RPC, is routed only by the front-layer adapter — `src/host-compat/` detects and buckets channels, while `src/client/host-compat/versions/` holds **one folder per set of verified facts** (`v0-2-0-rc-1` / `v0-2-0-rc-2` / `v0-2-1` / `unknown`), each describing that version's panel mechanics, header slot keys, plugin-page shape and where a menu's paint lives. A folder is keyed to what its facts cover, not to the shape of the version number: `0.2.0-rc.1` and `0.2.0-rc.2` are one prerelease channel, but rc.2 renamed the token a sticky menu group heading paints from, so the line carries two folders and the newer one is named for the build that opened it. A newer build whose facts did NOT move joins the folder that held instead of opening one of its own — the whole `0.2.1` line (`alpha.1` … `rc.3`) sits in `v0-2-1` for exactly that reason, with one `SUPPORTED_RELEASES` row per checked build so each verdict reads `exact` rather than `line`. Base code just asks the adapter questions (who owns the guide surface, which layer carries the blur, which token fades a menu) and never compares version strings. Another build on a verified patch line (a future `0.2.1-rc.4` against a table checked through `rc.3`) keeps that line's adapter, and a patch line nothing was checked against clamps to the nearest one with a log line saying so — which is the arm that now serves every release above `0.2.1-rc.3`. Only a release that will not parse at all falls into `unknown` and probes the DOM shape instead of guessing (`:has()` dual arms); supporting a new host means adding one folder and registering it.
|
|
163
156
|
- **[DSHA](https://github.com/DSH-APP/DSHA)** — DeepSeek Harness Android launcher (ROOT-free, Termux-free). Its bundled `dsh` is `0.1.5-rc.2`, BELOW this plugin's supported floor since v0.3.7: the peer gate refuses it at composition, so the plugin never loads there rather than restyling that shell with 0.2.x facts.
|
|
164
157
|
- **[deepseek-harness-desktop](https://github.com/anywhere-labs/deepseek-harness-desktop)** — Supported
|
|
165
158
|
|
package/README.zh.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<a href="https://www.npmjs.com/package/dsh-any-background"><img alt="npm 版本" src="https://img.shields.io/npm/v/dsh-any-background?color=4d6bfe"></a>
|
|
5
5
|
<a href="https://www.npmjs.com/package/dsh-any-background"><img alt="npm 月下载量" src="https://img.shields.io/npm/dm/dsh-any-background?color=4d6bfe"></a>
|
|
6
6
|
<a href="https://github.com/Tkingxiao/dsh-any-background/blob/main/LICENSE"><img alt="License: MIT" src="https://img.shields.io/npm/l/dsh-any-background?color=4d6bfe"></a>
|
|
7
|
-
<a href="https://www.npmjs.com/package/@deepseek-ai/dsh?activeTab=versions"><img alt="支持的 DSH 版本:0.2.0-rc.1 ~ 0.2.1-rc.
|
|
7
|
+
<a href="https://www.npmjs.com/package/@deepseek-ai/dsh?activeTab=versions"><img alt="支持的 DSH 版本:0.2.0-rc.1 ~ 0.2.1-rc.3 及其上" src="https://img.shields.io/badge/DSH-0.2.0--rc.1%20~%200.2.1--rc.3%2B-4d6bfe" /></a>
|
|
8
8
|
<a href="https://github.com/topics/dsh-better-sidebar"><img alt="插件生态:GitHub topic dsh-better-sidebar" src="https://img.shields.io/badge/%E6%8F%92%E4%BB%B6%E7%94%9F%E6%80%81-topic%20dsh--better--sidebar-4d6bfe" /></a><br /><br />
|
|
9
9
|
<a href="https://github.com/Tkingxiao/dsh-any-background"><img src="https://img.shields.io/github/stars/Tkingxiao/dsh-any-background?style=social" alt="GitHub stars"></a>
|
|
10
10
|
<a href="https://dsh.directory/plugins/tkingxiao/dsh-any-background"><img src="https://dsh.directory/badges/listed.svg" alt="dsh.directory listed"></a>
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
[English](README.md) | 中文
|
|
14
14
|
|
|
15
|
-
一个 **DeepSeek Harness** 外观插件:自定义主题色、背景壁纸(图片 / 视频 / 算法动态生成),以及逐表面的透明度与模糊度控制。兼容 **DSH `0.2.0-rc.1` 直至 `0.2.1-rc.
|
|
15
|
+
一个 **DeepSeek Harness** 外观插件:自定义主题色、背景壁纸(图片 / 视频 / 算法动态生成),以及逐表面的透明度与模糊度控制。兼容 **DSH `0.2.0-rc.1` 直至 `0.2.1-rc.3`,其上所有版本一并放行**(官方右侧栏「主题」卡片等界面按宿主是否提供右侧栏注册表扩展点自动启用,缺失时静默跳过)。
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
@@ -80,9 +80,9 @@
|
|
|
80
80
|
- **标题栏(会话标题行)** — 会话顶部那条标题 / 操作 / 视图页签行终于有了自己的一对滑块。宿主自身不给它任何底色(中心列的底色直接透上来),所以壁纸一起来,标题与页签就直接压在图上、和周围被蒙住的表面割裂。现在它按「区块淡色」作画,配方与对话文本框那张卡一致:材质取插件主背景面的颜色、按本部件自己的透明度上色,读起来是压在列上的一条带,而不是叠上去的第二层颜色;模糊落在行前的一块 `::before` 垫层上(配合 `isolation:isolate`——它是层叠上下文,但**不是** backdrop root;0.3.8 之前霜直接压在行身上,而行一旦带上 `backdrop-filter` 就成了 backdrop root,恰恰把挂在行里的那些浮层的磨砂压成一片平色);而这道霜仍然把行变成层叠上下文,所以行同时带上补偿性的 `z-index:10`——行内挂出的浮层(直到 `0.2.1-alpha.1` 的后台任务列表、每个 release 上都可能就地的日程任务菜单与「会话日志」`…` 菜单)本来用 `z-index:100`,否则会被关在行里、压到对话之下。10 高于它会重叠到的列内部件(输入位 9、回到底部 8),又远低于所有页面级表面(100 / 1000 / 1100),因此打开菜单时这一条的颜色和模糊都不会变;`z-index` 无需 `position`——该行本是 flex 子项,`z-index` 天然生效,而垫层要用的 `position:relative` 只在模糊非 0、且宿主把这一行留在 static 时才以内联方式写入。详见 v0.3.7 / v0.3.9。Windows 上它驱动的条带与侧栏分段(见上一条);模态打开时条带会跟着宿主蒙版自行变暗(宿主蒙版故意停在标题区下方);生成出来的那一段只负责绘制——`pointer-events:none` 且不声明 app-region——因此不会吞掉本该给原生标题栏菜单的点击(issue #29)。它还画在控件之下(`z-index:-1`),并自带墨色(`--dsh-any-caption-ink`):即使侧栏透明度很低、条带发浅,开合按钮与「应用 / 编辑」菜单也清楚可读;而当条带的明暗与主题方向冲突时,由「标题条带文字」裁定谁获胜——「自动」沿用条带推导,「跟随主题」完全不写覆盖(回到宿主的标签墨色,即 0.3.7 及之前),「始终白色 / 始终黑色」直接钉死(issue #30)。这一行是**运行时认领**的:插件从 `conversation.session.header*` 槽位锚点向上找到最近的那个 `<header>` 再打上自己的类(与卡片、帧同款做法),不猜哈希类名、也不依赖宿主把槽位摆在哪个层级;`0.1.7-alpha.1` 之前宿主把槽位裸挂在会话根上、没有这一行元素,那种宿主上插件什么都不会认领,部件自然无效果。桌面壳(Windows)上这一对滑块同时驱动窗口标题条带——**但左侧栏正上方的那一段除外**,那一段归「侧栏」这对滑块管(见下条);原生最小化/最大化/关闭那块改由取色探针报 `transparent`,让 Windows 不画自己的背景,露出的正是条带本身。空会话时宿主把该行标为 `headerBlank`(一条只留 padding 的占位),插件跳过认领。
|
|
81
81
|
- **侧边栏「主题」页面(双模式)** — 同一套五页(色彩 / 界面 / 字体 / 背景 / 配置)注册到两处:未安装 dsh-better-sidebar 时,通过官方右侧栏的公开扩展点(`sidebarRightTabs` + `sidebar.right.pane.tab`)在**官方侧边栏**的引导页挂一张「主题」卡片,点开即五页;安装了 dsh-better-sidebar 时,改为在它的侧边栏注册同名页面,官方引导页上的卡片自动撤下,避免重复。两处(加设置面板共三处)共用同一套页面代码与同一份状态,改一处处处同步;外壳按面板宽度自适应,窄面板收紧内边距并回到单列。宿主没有右侧栏扩展点时静默不注册,不影响任何其它功能。
|
|
82
82
|
- **对话视图卡片** — 消息列表自动包裹为半透明卡片,轨迹页可整页调节透明度与模糊,让壁纸从内容后方透出来。
|
|
83
|
-
- **主题导出 / 导入** — 一键导出为自包含的 `dsh-any-theme.json`(配置 + 壁纸,视频以 data URL
|
|
84
|
-
- **外观预设与配置档案** — 六套一键预设(默认 / 毛玻璃 / 极简白 / 暗夜紫 / 赛博 /
|
|
85
|
-
- **壁纸轮换** — 把多张图片加入轮换池(缩略图选择器),按随机或顺序、每次刷新 / 每天 /
|
|
83
|
+
- **主题导出 / 导入** — 一键导出为自包含的 `dsh-any-theme.json`(配置 + 壁纸,视频以 data URL 内嵌,v0.4.0 起连同整个轮换池的图片字节),可随时导入还原。
|
|
84
|
+
- **外观预设与配置档案** — 六套一键预设(默认 / 毛玻璃 / 极简白 / 暗夜紫 / 赛博 / 暖阳),外加自定义命名档案:随时保存当前观感、随时套回,支持重命名与复制,删除有二次确认保护。
|
|
85
|
+
- **壁纸轮换** — 把多张图片加入轮换池(缩略图选择器),按随机或顺序、每次刷新 / 每小时 / 每天 / 每周的频率自动更换,页面开着也会按时推进(不再只在刷新时更换);切换时服务端把所选图片复制进当前壁纸槽,导出/导入与取色管线完全不用改。
|
|
86
86
|
- **昼夜自动切换** — 指定日间与夜间两套配置,按固定时段或跟随系统深色模式自动切换。
|
|
87
87
|
- **自定义字体** — 上传一个 ttf / otf / woff / woff2 字体文件(上限 100 MB),通过 `@font-face` 应用到整个界面的文字;可随时停用或移除。字体按原始字节流式上传并持久化到插件数据目录,代码块仍保持等宽字体。
|
|
88
88
|
- **分组文字描边** — 沿用界面页的分组(现为十一组,含顶栏选项与标题栏),为每个分组单独加 `-webkit-text-stroke` 描边:粗细 0–4 px(0 = 关闭),颜色支持自动反色 / 灰 / 黑 / 白 / 主题色 / 自定义。代码块、`inline code`、图标,以及宿主那类 `background-clip: text` 的流光文字(「深度求索中」状态行与轮次过程行)自动豁免——多色语法不会糊成一团,渐变文字也不会被描边压成一坨纯色。
|
|
@@ -93,26 +93,19 @@
|
|
|
93
93
|
|
|
94
94
|
## 更新日志(只保留最近两个版本)
|
|
95
95
|
|
|
96
|
-
### v0.
|
|
96
|
+
### v0.4.1(修复:「每次刷新」档位壁纸每 30 秒自动切换)
|
|
97
97
|
|
|
98
|
-
-
|
|
99
|
-
-
|
|
100
|
-
- **「标题栏」这一行的霜不再吞掉子层的模糊。** `backdrop-filter` 让标题行同时成为层叠上下文和 BACKDROP ROOT,于是挂在行内的就地浮层能采到的背景只剩这一行自己画的东西——一片平色——恰恰是这条从行上垂下来的面板,模糊滑块看起来完全失效。现在霜改走 `::before` 垫层(配 `isolation:isolate`:是层叠上下文,但**不是** backdrop root),也就是本插件给帧、输入位胶囊和各个部件列一直使用的配方;行继续保留补偿性的 `z-index:10`,而垫层需要的 `position:relative` 只在模糊非 0、且宿主把行留在 static 时才以内联方式写入。在真实 `dsh web` 里(配置档直接 link 本工作树,用界面页上真正的「顶栏选项」滑块驱动):就地的 `…` 菜单与 portal 出去的「更多打开方式」菜单,在透明度 100 / 模糊 4 时都是 `rgba(243,247,252,1)`,在透明度 8 / 模糊 44 时都是 `rgba(243,247,252,0.08)` + `blur(44px)`。再用「跑真实插件代码 + 宿主安装产物 CSS」的浏览器复测:行的 `backdrop-filter: none`、其 `::before` 报告滑块的模糊值,而所有头部浮层形状(portal 的 dense `MenuSurface`、就地的 dense、就地的非 dense、无 role 的后台任务表面、子代理血缘树)都取「顶栏选项」的底色与磨砂,不再落到卡片组。
|
|
101
|
-
- **全新安装下界面透明度滑块不再是死的。** `readHostOpacityTokens()` 采的是 `getComputedStyle(:root)`,而 `design-platform.css` 把这一整套表面令牌声明在 `body { … }` 块里,自定义属性只会向下继承——于是没有选定主题色时每次读取都是空,`applyCustomTokensNow` 走 `surfaces === null` 那条出口,侧栏 / 卡片 / 输入框 / 顶栏选项的 alpha 从头到尾没被写出来。现在改从 `body` 采样,按宿主代次(它自己的深色标记 + 平台属性)缓存,并且读取时把插件自己那份 `body{}` 令牌表临时关掉——否则第二次采样拿回来的是插件自己刚写进去的字面量,表面颜色会冻结在第一次采到的那个值上,不再跟随宿主。
|
|
102
|
-
- **菜单模糊滑块拉到 0,现在就是没有磨砂。** 0.3.7 加过一个 `<html>` 类门控(`dab-card-frost` / `dab-header-frost`),让停在 0 的滑块不去抹宿主自己的 `--dsw-menu-backdrop-filter: blur(40px) saturate(150%)`(元素自身声明恒胜继承,所以未门控的 `var(--x, none)` 确实会清掉它)。事后看这个取舍是错的:它把滑块的**下端变成了最糊的一端**(0 → 宿主的 40px,1 → 1px),于是「把模糊度关掉」根本够不到,用户恰恰在那个位置上觉得控件是死的。现在这个 frost 令牌在每个取值上都被声明,0 时由 `none` 兜底胜出,门控机制整体删除。不会有人因此丢掉宿主观感:每个部件的默认值都是 30px,全新安装依旧是磨砂的,只有明确拖到 0 才会清掉。
|
|
103
|
-
- **版本表不动。** 上面这些形状都对着安装产物里的 `0.2.0-rc.1`、`0.2.1-alpha.3`、`0.2.1-rc.2` 三个构建复核过:两个 utilities 控件、`MenuSurface` 的 `data-menu-material` + `.material` 绘制层、菜单令牌集合三处一致,`0.2.1` 适配层的事实继续成立。
|
|
98
|
+
- **发生了什么。** v0.4.0 让 30 秒一次的定时检查顺带推进到期的轮换,好让「每小时」档在页面长驻时也能换图。但到期数学对「每次刷新」档位是**恒为到期**——这正是页面加载能拿到新图的原因(服务端在 `read` RPC 里推进它)。把它接进定时器后就成了:轮换开启 + 选了「每次刷新」→ 壁纸每 30 秒自己换一张;系统明暗翻转、开启昼夜定时时也会跟着再切一次(这两处同样驱动该定时器)。
|
|
99
|
+
- **怎么修的。** 「哪些档位参与活推进」现在是共享数学的一部分:`src/shared/rotation.ts` 新增 `rotationAdvancesLive()`(与其余到期数学一样构建时内联进两个 bundle),只有时间档位——每小时 / 每天 / 每周——参与活推进。「每次刷新」回到它原本的语义:每次页面加载换一张,外加手动「换一张」按钮。boot 回退与开启开关时的立即换图行为不变。
|
|
104
100
|
|
|
101
|
+
### v0.4.0(持久化正确性批次 + 轮换 / 导出 / 档案功能)
|
|
105
102
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
-
|
|
109
|
-
-
|
|
110
|
-
-
|
|
111
|
-
-
|
|
112
|
-
- **条带是「两个主人」,这是设计。** 左侧栏上方那一段归「侧栏」这一对滑块(同一份材质、同一份模糊镜像);侧栏宽度右侧全部归「标题栏」。所以移动标题栏滑块只会改变右段(issue #29 第 3 条)——分界就是宿主发布的 `--dsh-windows-sidebar-width`,拖动或折叠侧栏时它自己会跟着走。
|
|
113
|
-
- **侧栏重新读作「一个表面」,它的顶部与下方不再分层。** `--dsw-specific-sidebar-fill` 是宿主以**不透明**发布的主题令牌,而侧栏这个盒子里有三个元素各自声明了 `background:var(--dsw-specific-sidebar-fill)` 互相叠涂:`AppFrame` 的 `.sidebarCol`(网格列)、其内 `SidebarRoot` 的 `.root`(外壳本身)、以及工作区树那条 `.fade`(24px 渐变到底色)。底色不透明时叠几层看不出来,一交给「侧栏」透明度滑块就不是了;桌面端尤其明显——侧栏上方那条标题带是本插件自己的板子,用同一个变量、同一个不透明度只画一次,于是条带一个浓度、下面的列另一个浓度(就是这次桌面端反馈的分层)。现在这个令牌在列的**后代**上被重新声明为 `transparent`(`SIDEBAR_ONE_LAYER_RULE`),让最外层那个盒子成为唯一的绘制者——这也是宿主自己在 macOS 上的选择,`SidebarRoot.module.css` 正是为此把 `.root` 的底色清空。用宿主安装产物搭的 mock 实测:修前列 0.22 叠 `.root` 0.22(≈0.39)对 0.22 的条带;修后处处都是同一个 0.22。绑定用的是 CSS-Module 的类名形状(`[class*="_sidebarCol"]`,`0.2.0-rc.1` / `0.2.1-alpha.3` / `0.2.1-rc.2` 三处一致),所以哪个构建改了名,就只是回到未改动的宿主样子。
|
|
114
|
-
- **插件安装对话框里的「npm 源」面板保持宿主的不透明度。** `<fieldset data-install-registry>` 被 `createPortal` 到 `document.body`,是一个 `position:fixed; z-index:1100` 的面板,底色取自 `--dsw-alias-bg-layer-2`(面板自身与它的开关)和 `--dsw-alias-bg-layer-3`(展开态开关、手填地址的输入框)——这些都是「卡片」滑块全局接管的令牌,于是这块面板跟着卡片滑块一起变透明;而宿主写在那条规则上的注释恰恰相反:这层之所以不透明,是为了"让源选择表单控件压在对话框上仍然可读"。它因此并入 `applyExemptDefaults` 已经在维护的那组豁免(确认对话框、会话行菜单):保持当前主题色、把 alpha 钉在 1,不撤模糊也不改字色,其它一律不动;钉的值同样要覆盖其子树,因为自定义属性只会向下传播,选项就在里面。
|
|
115
|
-
- **`0.2.1` 整条线合成一个渠道,并逐构建点名。** `0.2.1-alpha.3` 把后台任务列表改画到共享的 `MenuSurface` 上——表面根没有 `role`,底色与磨砂落到了它的 `.material` 子层——所以运行时标记器现在也会认领 body 级的 `[data-menu-material]` 表面,顶栏选择器组同步加了对应分支;更早那种自己上色的 `<ul class*="_menu">` 形状继续匹配。从 `alpha.3` 到 `rc.2` 逐 tag 核对:适配层发言的文件一个都没动(`design-platform.css`、`MenuSurface`、`MenuGroup`、`AppFrame`、`ConversationRoot`、`TaskMenu`、`preload-windows`、五个头部槽位)。`SidebarRight.module.css` 只给面板外层加了 `overflow:clip`(fixed 浮动子节点仍会逃逸),插件页重构过但三个列表锚点都在。于是 `0.2.1-alpha` 与 `0.2.1-rc` 合为一个 `0.2.1` 渠道,覆盖 `alpha.1`…`rc.2`,每个构建一条 `SUPPORTED_RELEASES` 行(均为 `exact`),`engines`/peer/`dsh.compatibility` 逐一点名,事实目录改名为 `v0-2-1`。
|
|
103
|
+
- **主题导出现在带上整个轮换池(v3)。** 旧导出只带配置与主壁纸:轮换池的元数据(文件名 + 缩略图)随配置走了,图片字节却留在原机器上,换机导入后每次切换都报 `file missing`。v3 导出把池内每张图的字节(从新路由 `/dsh-any-background/rotation/file` 取回,名称走与配置清洗相同的防穿越围栏)内嵌为 `pool`;导入走新的 `rotationRestore` RPC 整体重建——字节按导出时的原文件名落盘、清掉未被引用的旧文件、游标夹取进新范围。v2 旧文件里的死条目会在导入时清空,而不是留着每次激活都失败。
|
|
104
|
+
- **轮换新增「每小时」档,且页面开着也会推进。** 到期推进原来只发生在页面加载时的 `read` RPC 里,长驻窗口永远不换图;现在 30 秒一次的定时检查也会过一遍轮换(纯数学门控,没到期就是空操作)。日 / 周 / 随机的到期与选图数学同时抽成双端共享的 `src/shared/rotation.ts`(构建时内联进两个 bundle),两端不会再在日 / 周边界上各自为政。
|
|
105
|
+
- **配置档案支持重命名与复制。** 每条档案新增行内重命名(Enter 提交)与一键复制(新 id、`(2)` 名字后缀,沿用 20 条上限的最旧淘汰)。
|
|
106
|
+
- **持久化正确性修复——每条都对应一个已发生过的故障模式。** 首启或读盘瞬时失败时 `readConfig` 返回的浅拷贝会被轮换增删原地污染,模块级默认配置从此带上幽灵池项(改 `structuredClone`);配置写失败后按 mtime 的内存缓存与磁盘分叉(改为写前丢缓存);字体槽收养杂散文件时按文件名错标 MIME,woff2 字节被当 ttf 服务(改为嗅探真实魔数并回写配置);壁纸裸字节上传不验内容(补上与 URL 下载、字体上传相同的魔数围栏);轮换池文件名毫秒碰撞(加随机后缀);手动激活轮换后游标不落盘、刷新回跳旧图(服务端按「合并进最新配置」回写,与自动推进同一套语义)。
|
|
107
|
+
- **结构与性能。** 浏览器半侧四个 body 级 MutationObserver 合并为单一总线(`src/client/dom-watch.ts`,最后一个退订自动断开);parts 观察的合批从 rAF 挪到微任务——对齐 v0.3.9 的教训(不绘制的窗口里排队的帧根本不跑),后台挂载的视图与 takeover 卡现在立即补上样式;`applyWp` 扇出去重(产出物 / 顶栏浮层等 pass 从每次两遍降为一遍);配置写失败先重试一次(Windows 上杀软扫描文件造成的瞬时 EBUSY)再明确告警,不再静默吞掉;`read` 的三个槽位并行探测;首启只写一次配置;启动时清扫崩溃残留的 tmp 文件、超额的 `.corrupt-*` 归档(保留最近 3 份)与轮换池孤儿文件(10 分钟宽限期);轮换推进 / 激活改用 `copyFile`,多 MB 壁纸不再整块过内存。
|
|
108
|
+
- **i18n 清理。** 删除四个无引用死键,英文文案里内嵌的中文引号改为英文写法;中英 197/197 键位对齐。
|
|
116
109
|
|
|
117
110
|
## 安装
|
|
118
111
|
|
|
@@ -156,8 +149,8 @@ pnpm dsh web
|
|
|
156
149
|
|
|
157
150
|
## 兼容性
|
|
158
151
|
|
|
159
|
-
- **[`dsh web`](https://github.com/deepseek-ai/deepseek-harness) `0.2.0-rc.1` ~ `0.2.1-
|
|
160
|
-
- **版本适配隔离**:release 由 Node 半侧从进程自身那份 `@deepseek-ai/dsh/package.json` 读出(客户端上下文不暴露版本;`ctx.profileContext.installAnchor` 指不到时退回启动器的磁盘布局),经 `read` RPC 下发后只由前置适配层路由——`src/host-compat/` 负责探测与渠道归类,`src/client/host-compat/versions/` 下**每个事实集合一个目录**(`v0-2-0-rc-1` / `v0-2-0-rc-2` / `v0-2-1` / `unknown`),各自描述该版本的面板力学、头部槽位键、插件页形状,以及菜单的底色与磨砂落在哪一层。目录按它覆盖的事实命名,不按版本号的形状:`0.2.0-rc.1` 与 `0.2.0-rc.2` 同属一个预发布渠道,但 rc.2 改了吸顶菜单分组标题取色的令牌,于是这条补丁行上并着两个目录,较新的那个按开启它的那个构建命名。**事实没变的更新版本不新开目录,而是并入那份仍然成立的事实目录**——整条 `0.2.1` 线(`alpha.1` … `rc.
|
|
152
|
+
- **[`dsh web`](https://github.com/deepseek-ai/deepseek-harness) `0.2.0-rc.1` ~ `0.2.1-rc.3`,其上所有版本一并放行** — `engines.dsh`、受门禁的 `@deepseek-ai/dsh-*` `peerDependencies` 与 `dsh.compatibility.dsh` 先逐一列出本插件核对过的构建(`0.2.0-rc.1`、`0.2.0-rc.2`、`0.2.1-alpha.1` … `0.2.1-rc.3`),再以不设上界的 `>=0.2.0-rc.1` 收尾,让其后每一个 release 都能加载。不设上界是有意选的:宿主强制的正是这份 peer 列表——范围没写到的版本会在导入任何模块之前被整行禁用——一条写到头的范围等于在一款还能正常工作的插件前面砌墙。没见过的 release 会取最近的一份事实,并在日志里写清它离 `exact` 有多远;一份过期事实最坏的代价是某个滑块不再淡出宿主已经搬走的那个表面,而一道失败关闭的版本检查代价是整个插件。`dsh.compatibility.dshReleases` 记录哪些构建被核对过、怎么核对的:列表里的每个 release 都是按 tag 逐包比对其事实目录核实的。这道门禁也意味着安装或更新插件后仍需重启 `dsh web`——检查发生在 profile 组装时,而不是插件更新时——但不设上界的总括,正让已经升到更新宿主的机器不再需要先等插件发版(或手工授予精确版本豁免 `dsh plugin allow-version`)。宿主版本在运行时由 Node 半侧解析,依赖特定宿主版本渠道的功能(右侧栏面板模糊、官方侧栏「主题」卡片)只在对应宿主结构存在时启用,其余功能在整个范围内表现一致。**`0.1.5` / `0.1.6` / `0.1.7` 这一档已移出兼容范围**:那些事实目录断言的东西已经没有再被核对,而一张没人核对的表比没有表更糟;范围从 `0.2.0-rc.1` 起步,意味着更旧的宿主会在组装期被明确拒绝,而不是被一份错的适配事实悄悄改版式。
|
|
153
|
+
- **版本适配隔离**:release 由 Node 半侧从进程自身那份 `@deepseek-ai/dsh/package.json` 读出(客户端上下文不暴露版本;`ctx.profileContext.installAnchor` 指不到时退回启动器的磁盘布局),经 `read` RPC 下发后只由前置适配层路由——`src/host-compat/` 负责探测与渠道归类,`src/client/host-compat/versions/` 下**每个事实集合一个目录**(`v0-2-0-rc-1` / `v0-2-0-rc-2` / `v0-2-1` / `unknown`),各自描述该版本的面板力学、头部槽位键、插件页形状,以及菜单的底色与磨砂落在哪一层。目录按它覆盖的事实命名,不按版本号的形状:`0.2.0-rc.1` 与 `0.2.0-rc.2` 同属一个预发布渠道,但 rc.2 改了吸顶菜单分组标题取色的令牌,于是这条补丁行上并着两个目录,较新的那个按开启它的那个构建命名。**事实没变的更新版本不新开目录,而是并入那份仍然成立的事实目录**——整条 `0.2.1` 线(`alpha.1` … `rc.3`)正因如此都坐在 `v0-2-1` 里,每个核对过的构建各占一条 `SUPPORTED_RELEASES` 行,判定都是 `exact` 而不是 `line`。底码只向适配层提问(引导页表面归谁、模糊挂在哪一层、菜单该重写哪些令牌),不比较版本字符串。同一补丁行内的其它构建(将来的 `0.2.1-rc.4` 之于核对基准 `rc.3`)用该行的档位;整条补丁行都不在已核对范围内才收边取最近的一档并写日志——`0.2.1-rc.3` 之上的每一个 release 如今就是靠这一档加载的。只有解析不到 release 时归入 `unknown`,回退到按 DOM 形状探测(`:has()` 双臂)而不是猜一个版本;适配新宿主 = 新增一个版本目录并在注册表登记。
|
|
161
154
|
- **[DSHA](https://github.com/DSH-APP/DSHA)** — DeepSeek Harness 安卓启动器(免 ROOT、免 Termux)。其包内 dsh 为 `0.1.5-rc.2`,自 v0.3.7 起**低于本插件的兼容下界**:peer 门禁会在组装期拒绝它,所以插件在那上面根本不加载,而不是拿 0.2.x 的事实去改版那个壳。
|
|
162
155
|
## 权限、副作用与边界
|
|
163
156
|
|