PluginWorld
Ds

dsh-any-background

dsh✓ SPEC VERIFIED

Appearance plugin for DeepSeek Harness: custom theme color (PS-style color wheel), background wallpaper with opacity/blur controls, and separate main/settings interface opacity.

@Tkingxiao · v0.4.2 · MIT · updated 6d ago

SECURITY

A

SCORE

79

INSTALLS

▲ 6.6K

PLUG IN

npx @deepseek-ai/dsh web

Launch, then search "dsh-any-background" in the built-in market to plug it in

README

dsh-any-background

npm version npm monthly downloads License: MIT Supported DSH versions: 0.2.0-rc.1 ~ 0.2.1-rc.3 and above Plugin ecosystem: GitHub topic dsh-better-sidebar

GitHub stars dsh.directory listed

English | 中文

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).


Screenshots

Custom homepage
Custom homepage · wallpaper + theme color applied

Theme color picker
Theme color picker · PS-style wheel + precise HSL/RGB inputs

Per-part opacity and blur
Per-part opacity and blur · main background, sidebar, cards, settings

Background editor
Background editor · image/video wallpapers support drag-to-pan and scroll-to-zoom

Generated dynamic background
Generated dynamic background · mesh gradient / Shader / geometric presets

Geometric background, low-poly mode
Generated dynamic background · geometric low-poly mode preview

Config export and import
Export and import configs to share

Features

  • PS-style Color Wheel — Pick hue on the ring, adjust saturation & lightness in the inscribed square. Generates 30+ CSS design tokens in real time.
  • Precise HSL / RGB Input — Enter exact color values numerically with instant bidirectional sync to the wheel.
  • Smart Color Extraction — One click derives a theme color from your wallpaper by sampling the visible region, quantizing, and filtering out gray / near-black / near-white pixels. Video wallpapers contribute via an auto-captured frame. Fully client-side.
  • Eyedropper — Hover the wallpaper to preview a color and click to pick it as the theme color.
  • Background Wallpaper — Upload any image as your wallpaper. Drag to pan and scroll to zoom inside a viewport-proportional editor (one finger to pan, two to pinch-zoom on touchscreens).
  • Video Wallpaper — Use a video as a live wallpaper: muted looping playback that survives refreshes (file persistence + HTTP streaming with Range seek), with an auto-captured frame powering the preview, theme-color extraction, and the position editor.
  • Position Editor — One shared editor for images and videos: drag to pan, scroll or pinch to zoom, one-click reset. Image and video placements are stored separately and never overwrite each other.
  • Layout Modes — Fit / Fill / Stretch / Tile / Center for both images and videos; in Fit mode the editor-committed framing stays consistent across window resizes and cross-monitor moves.
  • Generated Dynamic Backgrounds — Choose mesh gradient, Shader, or geometric patterns with adjustable spread, intensity, and seed locking.
  • Per-surface Interface Opacity — Independent sliders for the main background, sidebar, cards & panels (including the dropdowns and menus around the dialog), the input & controls (composer box, Cordis panel), the settings panel, the conversation text frame, the trajectory view, the right sidebar (or bettersidebar), produced / highlighted content, the header popovers (Agent Team panel, background-job list and the session-header dropdowns), and the header bar (the conversation's title / view-tab row).
  • Per-surface Interface Blur — Frosted-glass backdrop-filter blur (0–60 px) per surface, including a real backdrop on the composer, the Cordis panel and popover surfaces via stable host selectors. The slider IS the frost amount, and 0 means no frost — including over the host's own default (--dsw-menu-backdrop-filter: blur(40px) saturate(150%)). Through 0.3.7 a 0 meant "leave the host alone", which made the bottom of the slider the blurriest setting it could produce (0 = the host's 40 px, 1 = 1 px) and left "turn the blur off" unreachable; now 0 is 0, and because every part still DEFAULTS to 30 px a stock install keeps the frosted look unless someone drags it down.
  • Left sidebar + the caption band above it — On the Windows desktop shell the sidebar's opacity + blur pair drives the sidebar column AND the segment of the window caption strip that sits directly above it. The host paints that strip as one full-width box; the plugin cuts it at the sidebar's own published width (--dsh-windows-sidebar-width), so the left part carries the sidebar's material and frost and the rest stays the 「标题栏」 card's. Drag the sidebar wider, or collapse it to 0px, and the cut moves with it. The split is deliberate (issue #29, item 3): the left strip IS the sidebar column's own surface, so the header-bar sliders are not meant to reach it.
  • Produced / Highlights — Code blocks in conversation content (with their language banner), inline code highlight chips and produced chips share one opacity + blur slider. The opacity is the alpha of each surface's own background color (no second color stacked on top of the original), and the blur frosts that same layer so the wallpaper shows through the content.
  • Right sidebar / bettersidebar surface — One slider pair (panelOpacity / blurs.panel), two identities: without dsh-better-sidebar it reads "右方侧边栏" (Right sidebar) and drives the official right Sidebar's surface tokens and frosted blur (works on 0.1.5-rc.2 through 0.1.7); with dsh-better-sidebar installed it reads "bettersidebar" and takes over that plugin's bottom workbench panel (the official sidebar keeps responding too). The row is always visible.
  • Header popovers — The session-header dropdowns get their own opacity + blur pair: the Agent Team panel, the background-job list, the open-in-app / session-log menus and the subagent lineage tree. On 0.1.7 the open-in-app picker moved to a portal and the session-row menu became a dynamic slot, so the plugin observes the stable conversation.session.header* slot anchors and tags the open popover at runtime instead of relying on class shapes. A popover counts as the header's when it is portaled to <body> or mounted inside one of those anchors — the session-log … menu is the second kind (session-log-export/HeaderAction.tsx renders its Menu without portal), and until 0.3.9 only a fragile _denseList-class arm reached it.
  • Header bar (the session title row) — The row carrying the session title, its actions and the view tabs gets a slider pair of its own. The host paints nothing there (the centre column's background shows straight through), so once a wallpaper is up the title and tabs sat on the raw picture while every masked surface around them did not. It now paints as a region tint, the same recipe as the chat column's card: the material is the plugin's main-background surface at this part's own opacity, so the bar reads as one band over the column rather than a second colour stacked on it. The frost rides a ::before underlay behind an isolation:isolate on the row — a stacking context that is NOT a backdrop root, which is what lets the overlays hanging off the row frost the wallpaper at all (through 0.3.8 the filter sat on the row itself, and a filtered row is a backdrop root: exactly those panels were left blurring a flat tint). Because that context still localizes the row's paint order, the row carries a compensating z-index:10 — the overlays it hosts (the background-job list through 0.2.1-alpha.1, the schedule task menu and the session-log … menu on every release) hang out of it with z-index:100 and would otherwise be confined under the conversation. 10 clears the in-column chrome it overlaps (seat 9, back-to-bottom 8) and stays under every page-level surface (100 / 1000 / 1100), so nothing about the part's look changes when a menu opens. z-index needs no position — the row is a flex item, where it applies on its own — and the underlay's position:relative is written inline only while a blur exists and only when the host left the row static. See v0.3.7 / v0.3.9. On the Windows desktop shell the band it drives is split with the sidebar (previous bullet), dims itself while a modal is open (the host mask deliberately stops below the caption), and its generated segment is paint-only — pointer-events:none and no app-region declaration — so it can never swallow clicks meant for the native caption menubar (issue #29). It also paints below the controls (z-index:-1) and carries its own ink (--dsh-any-caption-ink), so the sidebar toggle and the 应用 / 编辑 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • Forced Interface Scheme — Force light or dark token palettes regardless of the accent color's lightness; in Auto both the surface and font directions follow the accent's lightness (dark pick → light fonts, light pick → dark fonts), falling back to the wallpaper's perceived brightness when no color is picked.
  • File-based Persistence — All settings are stored on the filesystem under ~/.dsh/.dsh-any-background-data/, not localStorage.
  • Bilingual — Full Chinese / English UI with automatic locale detection.
  • Theme Watchdog — Re-asserts the custom theme if the host resets it.

Changelog (latest two releases)

v0.4.2 (dsh-better-display's summary bar compatibility · fixes issue #32)

  • The summary bar is no longer hollowed out. Its plate reads the token our 「主界面」 opacity owns, so at 0 the conversation streamed straight through it. When dsh-better-display is detected the band is pinned at a 20% plate + 30px frost — no slider moves it, the color still follows the theme — and the three separate bars merge into one box with the bottom corners rounded.
  • Third-party surface self-check + a compatibility switch. At boot the plugin scans other plugins' stylesheets and names in the console whose surface reads a token this plugin owns: registered ones get protected automatically, unregistered ones log a note inviting that line to be reported. A card at the bottom of the Interface page switches the whole pin sheet off, leaving the other plugin to paint its own surfaces.
  • Issue #32 fixed. The frosted sidebar column is lifted to z-index:1, so a tooltip overhanging the sidebar's right edge stays crisp; the modal exemption now resets z-index and isolation together.
  • After installing or updating, restart the host once — the compatibility gate runs when the profile is assembled.

v0.4.1 (fixes the "Every refresh" cadence flipping the wallpaper every 30 seconds)

  • Only the time-based cadences (hourly / daily / weekly) advance on the schedule tick; "Every refresh" keeps its original meaning: one fresh pick per page load, plus the manual next button.

Installation

Method 1: npm install (Recommended)

# published on the npm registry
dsh plugin --profile web add dsh-any-background

# or straight from the GitHub repository
dsh plugin --profile web add github:Tkingxiao/dsh-any-background

Then launch:

dsh web

The plugin appears as a "Theme" section in Settings.

Method 2: npx (No Global Install)

npx @deepseek-ai/dsh plugin --profile web add dsh-any-background
npx @deepseek-ai/dsh web

Method 3: Local Build (Development)

The lib/ directory is committed, so installs need no build step. To rebuild after editing src/:

git clone https://github.com/Tkingxiao/dsh-any-background.git
cd dsh-any-background
pnpm install
pnpm run bundle
pnpm dsh plugin --profile web add "dsh-any-background"
pnpm dsh web

Compatibility

  • dsh web 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.
  • 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.
  • 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.
  • deepseek-harness-desktop — Supported

Permissions, side effects & boundaries

  • Integration form: official Profile Bundle — package.json declares dsh.bundle.patch: ./cordis.patch.yml (a loader insert layer), the repository ships prebuilt runtime artifacts ready to use (lib/index.js, lib/client.js), and there are no install scripts, no postinstall, no native binaries, and no build step at install time.
  • Filesystem: the server half reads and writes only inside <dsh home>/.dsh-any-background-data/ (config JSON, wallpaper, rotation pool, video, font) and touches nothing outside it; config writes are atomic (temp file + rename). These files live on the real disk, so they are outside generation restore — it neither captures nor rolls them back; deleting the directory is a full plugin reset.
  • Network: one outbound fetch happens only when the user pastes an http/https image or video URL and presses Apply; no telemetry, no other external calls.
  • Shell / native: none. No child_process, no native modules, no dynamically downloaded executables.
  • HTTP surface: registers only /dsh-any-background/{video,wallpaper,font} (GET/HEAD streaming) with matching */upload POST routes (100 MB cap) and the dedicated RPC channel /dsh-any-background under the local dsh web server; no extra listening ports.
  • Restart requirements: the first install needs a (re)start of dsh web to load the client bundle; settings changes afterwards apply live and persist automatically. Updating the plugin requires a restart to pick up the new lib/client.js.
  • Tests & verification: pnpm run typecheck (full tsc check) and pnpm run bundle (tsdown emits lib/); no automated unit tests — behavior is verified manually.
  • Known limitations: the styling relies on stable host DOM markers ([data-sidebar-right-panel], [data-dsh-bottom-panel], …) and CSS token names; a host restyle of those layers can leave a slider ineffective for its surface (cosmetic only — nothing breaks). The version-specific half of those selectors lives inside its own version folder, so a host revision normally means editing that one file. -webkit-text-stroke may clip about 1px at the edge of some single-line ellipsis containers. One rule is a deliberate cross-plugin accommodation: dsh-better-display paints its sticky reading lanes from the token our 「主界面」 opacity owns, so when that plugin is detected its lanes are pinned at 20% plate + 30px frost outside the sliders — if it ever renames those markers the rule stops matching and its lanes simply go back to being painted the way it paints them.

Star History

Star History Chart

License

MIT

SIMILAR PLUGINS