Themes & Appearance

@dsh-external/dsh-client-ui-skin-switcher

aminuosi/dsh-skin-switcher · ★ 0

aminuosi

Settings skin switcher for the dsh web GUI: lists installed skins (bundles shipping skin.json), lets the user pick one, and applies it on the next page refresh through the profile patch layer's live reload

License
MIT
Added
2026-10-30
Maintenance
active
Install kind
github
Manifest details
Manifest status
Declared
Manifest path
package.json#dsh
Manifest checked
2026-10-30T09:38:32.857Z
Lifecycle scripts
None found

Static manifest checks report declarations only; they are not compatibility or security review.

安装

dsh plugin --profile web add 'github:aminuosi/dsh-skin-switcher#5f9cec6174fbc078c8df88e49f140bb72d14aaed'

GitHub 源码安装:首次需按提示配置 allowBuilds 构建授权后重试

For install and environment setup, plus plugin development tutorials, see DSH community docs ↗

README

Source README · body: en · Read Chinese README

Table of contents
  1. Highlights
  2. Install
  3. Skin package protocol
  4. How it works
  5. Routes
  6. Development
  7. License

中文 · English

A skin switcher for the DeepSeek Harness (dsh) Web GUI — a Settings page that auto-discovers every installed skin, lets you pick one from a dropdown with hover previews, and applies it after a page refresh.

A pure Cordis plugin; no dsh source changes. Works out of the box with skin.json-based skins from the dsh-deep-whale ecosystem.

Highlights

  • Settings → Skins: a dedicated settings page (section id skin-switcher, chosen to avoid collisions with other skin managers).
  • Dropdown with hover previews: hovering an option shows a live preview thumbnail in a side panel (auto-picks light/dark per the active theme), plus name, tagline, author, and accent color.
  • Auto-discovery: any loader entry whose package ships a skin.json (or exports["./skin.json"]) becomes an option; newly installed skins appear after a restart — no registry, no code changes.
  • Mutually exclusive: exactly one skin is active at a time; Default theme restores the stock UI.
  • Clean activation: switching writes loader-row disabled state, so the next page refresh loads only the selected skin's client bundle — not "load everything, flip the visuals".
  • Surgical patch writes: only the managed block between marker comments in cordis.patch.yml is rewritten; hand-written entries and comments survive byte-for-byte. dsh's user-patch hot reload (watchUserPatches) recomposes the tree within seconds — no service restart needed.

Install

cd <harness>
dsh plugin --profile web add ../dsh-skin-switcher          # local checkout
# or
dsh plugin --profile web add github:<you>/dsh-skin-switcher # GitHub

Restart dsh --profile web, then open Settings → Skins: the dropdown lists Default theme plus every installed skin; hover to preview, click to switch, the page refreshes and applies.

Remove:

dsh plugin --profile web remove @dsh-external/dsh-client-ui-skin-switcher

Skin package protocol

Any dsh bundle is recognized as a skin — and appears in the dropdown — when it ships a skin.json (or exports["./skin.json"] pointing at one):

Field Meaning
id skin identity (required)
name / nameEn display name (zh/en)
author / tagline / description author, tagline, description
accent accent color (dropdown dot)
preview.light / preview.dark relative preview paths (webp/png/jpg/gif/avif/svg all supported)
order list ordering

And it must install as a dsh bundle (cordis.patch.yml inserting its skin row, declared via dsh.bundle.patch).

Reference skins: maid-atelier, dsh-client-ui-skin-claude.

How it works

  1. Discovery — the host half walks loader entries, resolves each package with a skin.json, and builds the catalog (live enabled state + preview URLs).
  2. Select — POST /skin-switcher/select enables the chosen skin row and disables every other, written into the managed block of ~/.dsh/profiles/<name>/cordis.patch.yml.
  3. Apply — dsh's user-patch hot reload recomposes the loader tree → client-modules recomputes window.__DSH_BOOT__ → the refreshed page loads only the selected skin's bundle, whose client apply() decorates the UI.

Routes

Method Path Description
GET /skin-switcher/catalog Installed skins (name/author/accent/preview URLs/enabled state)
POST /skin-switcher/select body { "skinId": <row id or null> }; null = default theme. Writes the patch and waits for recomposition
GET /skin-switcher/preview?pkg=<name>&mode=light|dark Preview image (correct content-type by extension, traversal-guarded)

Development

pnpm install
pnpm test       # vitest: patch writing, skin discovery, preview MIME (23 cases)
pnpm typecheck
pnpm build      # tsdown → lib/index.js (host) + lib/client.js (browser)

lib/ is committed distribution output, so dsh plugin add installs from a fresh clone without building.

License

MIT