sakka6868/dsh-skin

dsh-skin

DSH skin plugin: Alphacoders popular wallpapers as the DeepSeek Harness Web GUI background — gallery, search, local image upload, light/dark mask

待分类alphacoderscordisdeepseek

dsh-skin-alphacoders

English | 中文

A DSH skin plugin that turns Alphacoders popular wallpapers into the DeepSeek Harness Web GUI background, plus live wallpapers (video backdrops) from MoeWalls and Pexels. A dual-half (Host + Client) Cordis plugin following the DSH package conventions.

Wallpaper preview

Settings gallery

Features

Static wallpapers (Alphacoders)

  • Gallery: a "Wallpaper" settings section browsing the popular wallpapers (15 per page, pagination, lazy loading).
  • Search: keyword search via the Alphacoders search page (single page, up to 30 results); clearing the search restores the popular list.
  • One-click apply: click a thumbnail to apply it as the background; only the page skeleton (app frame, conversation root, sidebar) turns translucent through theme token overrides so the wallpaper shows through — every raised surface stays fully opaque (--dsw-alias-bg-layer-1/2/3 keep their product values), so cards, inputs, menus, modals and the changed-files hover preview keep their native contrast instead of blending into the photo.

Live wallpapers (video backdrops, v3)

  • MoeWalls / DesktopHut sources: switch between two video sources inside the live tab; browse thumbnails by category/search/pagination.
  • Download-based flow (v3.4): clicking a thumbnail opens the source site (moewalls.com / desktophut.com) in a new tab so you download the clip there, then set it via "Upload local image/video" — the plugin never streams from the source, so upstream rate-limiting can't stall the loop.
  • Local video upload (v3.4): accepts PNG/JPEG/WebP/GIF images and WebM/MP4 videos; a video becomes a looping live wallpaper stored under $DSH_HOME/storages/skin-alphacoders/, restored after restart; 200MB upload cap.
  • Seamless loop (v3.3): two <video> elements relay with an instantaneous swap (no cross-fade) — no stall and no dark/bright flash per cycle, close to Wallpaper Engine; glass blur / mask / fade apply to the video layer too.

Visuals & display

  • Glass blur (v3): a 0–30px blur applied to the wallpaper layer only — app surfaces and their text sit above it and stay sharp (glassmorphism without hurting legibility).
  • Ken Burns drift (v3.1): a slow zoom/pan keeps static wallpapers alive.
  • Layered gradient mask (v3.1): dimmed edges + translucent center + soft vignette + brand glow, all driven by one slider; white fog on light themes, black dim on dark, following the DSH theme (light/dark/system).
  • Wallpaper fade-in (v3.1): a new wallpaper fades in over 0.8s; slider drags and theme flips never flash.
  • Display controls: four fill modes (cover/contain/center/stretch) and a 0–90% mask strength for readability.

More (v2)

  • Per-theme wallpapers: the light and dark schemes each keep their own wallpaper and follow automatically when you switch light/dark.
  • Favorites: star any gallery card; favorites persist in user-data.json and apply with one click.
  • Auto change: periodic auto-swap — random (fresh remote picks), sequence (walk the favorites in order) or favorites (random favorite) — plus a "Change now" button; interval 1–168 hours.
  • History (v3.1): applied wallpapers are recorded (up to 5 entries, same-wallpaper entries deduped and moved to the top); one click restores an older backdrop.
  • Persistence: preferences live in the plugin's own document $DSH_HOME/storages/skin-alphacoders/settings.json (DSH 0.2 removed the plugin settings service the 0.4.2 line registered with), written atomically; favorites/history live in user-data.json next to it. Upgrading from ≤0.4.2 does not migrate the old settings.yaml section — pick a wallpaper once after the upgrade.
  • Performance: the background prefers the 1920px variant (thumb-1920-<id>) instead of downloading 5K/8K originals; gallery/search/live lists have in-memory TTL caches.
  • Clean unload: removing the plugin row removes its route, scheduler and styles; the GUI returns to the default look.

Install

# Installs the built package into the web profile
dsh plugin --profile web add dsh-skin-alphacoders

Option 2: build from source

# 1. Build this package (produces lib/)
pnpm install && pnpm build   # or npm install && npm run build

# 2. Install into the web profile
dsh plugin --profile web add "file:<absolute path to this package>"

Either way, register the plugin row in $DSH_HOME/profiles/web/cordis.patch.yml:

# $DSH_HOME/profiles/web/cordis.patch.yml
- insert:
    - id: skin-alphacoders
      name: 'dsh-skin-alphacoders'
      config:
        cacheTtlMs: 3600000      # optional: list cache TTL (ms)
        requestTimeoutMs: 20000  # optional: fetch timeout (ms)
        localMaxBytes: 209715200 # optional: local upload cap in bytes (200MB default)

Restart dsh web; the section lives under Settings → Wallpaper.

Dev tip: pnpm's file: dependency hardlinks a copy, so during iteration replace node_modules/dsh-skin-alphacoders with a directory junction to the source folder (or re-run dsh plugin add after each rebuild).

Architecture

┌─ Host half (node) ───────────────────────────────────────────┐
│  alphacoders: fetch + parse popular-wallpapers?page=N        │
│  (server-rendered schema.org ImageObject, 15/page)           │
│  moewalls (v3): REST list/categories → Chrome fallback →     │
│  HTML page; details resolve by page URL; video proxied +     │
│  disk cache (LRU 10)                                        │
│  desktophut (v3.5): Node-fetched home/category/search   │
│  pages, wallpaper-card parse (thumbnails accepted from   │
│  /images and /img; an empty home parse falls back to     │
│  /popular-live-wallpapers); download-based (no streaming)│
│  in-memory TTL caches (lists 6h, details 6h, categories 24h)│
│  settings.json store (own document, atomic write queue)      │
│  user-data.json (favorites ≤200, history ≤5, atomic write)   │
│  auto-change scheduler (setTimeout chain, per-mode pools)    │
│  HTTP routes (via ctx.webServer):                            │
│    GET  /skin-alphacoders/wallpapers?page=N | ?query=Q       │
│    GET  /skin-alphacoders/moewalls/{categories,list,detail,video} │
│    GET  /skin-alphacoders/desktophut/list                    │
│    GET  /skin-alphacoders/settings | POST (field-allowlist)  │
│    GET  /skin-alphacoders/user-data                          │
│    POST /skin-alphacoders/favorites (+ /favorites/remove)    │
│    POST /skin-alphacoders/history/remove                     │
│    POST /skin-alphacoders/theme / auto-change/now            │
│    POST /skin-alphacoders/local (image/video upload)         │
│    GET  /skin-alphacoders/health                             │
└──────────────────────────────────────────────────────────────┘
        same-origin fetch (no CORS surface)
┌─ Client half (browser) ──────────────────────────────────────┐
│  skin-store: local snapshot of the settings route + subscribe│
│  user-data store: favorites/history snapshot                 │
│  per-theme mirror: theme/change re-syncs the flat wallpaper  │
│  fields from the active scheme's slot (fromTheme marker)     │
│  background: body::before wallpaper layer + ::after mask     │
│              + video relay (two <video> elements, instant    │
│              swap, no dark/bright flash)                     │
│              + ctx.theme.overrideTokens surface translucency │
│              (signature-deduped against theme/change storms) │
│  settings.section "Wallpaper": static gallery + live gallery │
│  + upload + favorites + history + auto-change + theme tabs   │
└──────────────────────────────────────────────────────────────┘

Why the plugin owns its settings document

The api-proxy settings wire is a Host allowlist (locale, ui-theme, …); a namespace registered by a plugin outside the repository answers settings-not-exposed. DSH 0.2 then removed the plugin-side settings service altogether (ctx.settings.register / SettingsScope are gone), so the Host half owns a plain document instead — $DSH_HOME/storages/skin-alphacoders/settings.json, written atomically through a promise queue — and the browser half reads/writes it through the plugin's own HTTP route. The 0.4.2 ui-skin section of $DSH_HOME/settings.yaml is not migrated.

Configuration

FieldDefaultMeaning
cacheTtlMs3600000Gallery page cache lifetime in memory (ms)
requestTimeoutMs20000Timeout for fetching remote sites (ms)
localMaxBytes209715200Local upload (image/video) size cap in bytes
  • Wallpaper copyright belongs to their authors and Alphacoders / MoeWalls / DesktopHut; personal use only, no bulk downloading or redistribution.
  • Requires network access to those sites and their image/video CDNs.

Verification

e2e/verify.mjs is a Puppeteer (system Chrome) end-to-end check. Install the plugin and start dsh --profile web --port 3090 (a test instance can be targeted via the E2E_URL env var), then:

node e2e/verify.mjs

Covers (70 assertions): gallery render, favorites round-trip, per-theme wallpapers, light/dark linkage, persistence across reload, history restore (deduped), auto-change, glass blur/filter boost/Ken Burns/gradient mask, search, live list/pagination/site-open, local image & video upload, seamless video relay, local file cleanup, user-data persistence, no page errors.

Three source scripts accompany it: e2e/bench-desktophut.mjs (fetch layer, hits the real site), e2e/verify-live-sources-ui.mjs (UI layer, covers the static / MoeWalls / DesktopHut sources, needs E2E_PAGE_URL pointing at a tokenized instance) and e2e/net-probe-alt-sites.mjs (source reachability).

DesktopHut shows 0 wallpapers: triage order

DesktopHut listings come from server-rendered HTML, so a site redesign makes the parse fail silently — no error, just an empty grid. Work through fetch layer → Host route → UI:

# 1) Fetch layer (hits the real site, no instance needed; exit code 1 on failure)
node e2e/bench-desktophut.mjs
#    Expect: ALL PASS, with “all (cat="") p1” reporting items >= 3

# 2) Host route (PowerShell; `items` must not be an empty array)
Invoke-RestMethod "http://127.0.0.1:3080/skin-alphacoders/desktophut/list?page=1" | ConvertTo-Json -Depth 3
Invoke-RestMethod "http://127.0.0.1:3080/skin-alphacoders/desktophut/list?cat=anime-live-wallpapers&page=1" | ConvertTo-Json -Depth 3

# 3) Does a category slug still exist (a dead slug answers 404 and renders empty)
Invoke-WebRequest "https://www.desktophut.com/category/<slug>/" -Method Head -UseBasicParsing | Select-Object StatusCode

# 4) UI layer (needs a tokenized instance URL; asserts all three sources render a
#    grid whose thumbnails load without scrolling, and writes a screenshot)
$env:E2E_PAGE_URL='http://127.0.0.1:3090/?token=<token>'
node e2e/verify-live-sources-ui.mjs

Notes:

  • The default “All” tab browses https://www.desktophut.com. The site redesigns without notice (on 2026-09-25 its home page moved thumbnails from /images/… to /img/<W>x<H>/…); the parser now accepts both paths, and an empty home parse falls back to /popular-live-wallpapers.
  • Category slugs get renamed with no redirect. The table lives in DESKTOPHUT_CATEGORIES (src/fetch-desktophut.ts) and is shared by the Host validation route and the UI dropdown. To resync, read the /category/<slug> links off https://www.desktophut.com/show/all-categories and verify each one with the bench script above.
  • Empty pages are no longer cached: listings live in memory for 1 hour (cacheTtlMs), but as of v3.5.1 an empty page is not written to the cache, so the panel's retry button re-fetches for real. Changing Host code still needs a Host restart; hard-refresh the page as well — the client half changed too.
  • Switching live sources is now near-instant: when the wallpaper section opens it warms the first MoeWalls and DesktopHut pages in parallel (the Host caches per page and collapses concurrent requests), so the first switch no longer pays a cold fetch. A failed warm-up changes nothing — every source still loads on its own.