@ganrunlin-dsh/dsh-client-ui-bg
v0.1.1
Published
User-customizable web background: local file or remote URL, applied independently to AppFrame and the welcome-screen hero
Downloads
48
Readme
@ganrunlin-dsh/dsh-client-ui-bg
A DeepSeek Harness plugin that adds a user-customizable web background. Two preference rows live in the General settings panel — one for the window background, one for the welcome-screen background. Each row accepts a color, a remote URL, or a local file upload; the resolved value paints <body> before the page shell mounts, so the user never sees a flash of the unconfigured background.
Install
dsh plugin --profile web add @ganrunlin-dsh/dsh-client-ui-bgdsh plugin adds the package via pnpm and reconciles it into the profile's bundle stack — the package declares dsh.bundle.patch in its manifest, so reconcilePlugins() appends it to dsh.profile.bundles automatically. Restart the profile after install.
Requires
@deepseek-ai/dsh-web-app ≥ 0.1.1(any web profile). The plugin declares its DSH peers aspeerDependencies, so they resolve from the host installation — no version pinning needed on the user side.
What you get
A new Background section in Settings → General, with two cards:
- Window — paints
<body>for the whole AppFrame. - Welcome screen — paints the welcome shell's hero backdrop.
Each card exposes a four-button kind selector (Default / Color / URL / Upload) and an inline input that swaps with the kind.
Configuration
The ui-bg settings namespace; the Host half installs the schema through installSettingsSection, so any configuration surface that owns the ui-bg key (the Settings panel, host patches, the --patch CLI flag, etc.) can drive it.
| Field | Type | Default | Semantics |
|---|---|---|---|
| frame.kind | 'none' \| 'color' \| 'url' \| 'file' | 'none' | Window background source kind. |
| frame.value | string | '' | CSS color (#rgb / #rrggbb / #rrggbbaa), remote URL (http://… or https://…), or data URL (data:image/…). |
| hero.kind | 'none' \| 'color' \| 'url' \| 'file' | 'none' | Welcome-screen background source kind. |
| hero.value | string | '' | Same shape as frame.value. |
The frame slot aliases over --dsw-alias-bg-base for the whole AppFrame; the hero slot aliases over the same token, but the welcome shell paints its own backdrop inside the conversation view. When a slot is 'none', the next apply pass clears its alias and the theme default returns.
Programmatic override
# ~/.dsh/profiles/web/cordis.patch.yml
- id: ui-bg
config:
frame:
kind: color
value: '#1f2933'
hero:
kind: url
value: 'https://example.com/hero.jpg'Model Experience
None. This plugin owns presentation and persistence only; nothing here reaches a model request.
User Experience
- The user opens the General settings panel and sees two cards under Background: Window and Welcome screen. Each card exposes a four-button kind selector (Default / Color / URL / Upload) and an inline input that swaps with the kind.
- A color input commits
#1f2933as soon as the regex validates; a URL input commits when the value starts withhttp://orhttps://; a file input commits when the chosen file is ≤ 4 MB and matches one ofimage/png,image/jpeg,image/gif,image/svg+xml. Other shapes surface an inline error in the row and refuse the write at the schema'svalidatecallback. - Each write goes through
ctx.settingsScope.bind<BgSettings>(...), fences on the namespace revision, and persists to~/.dsh/profiles/web/settings.yaml. The Host-sideapply()re-reads on every index-html tap and re-emits the inline boot script, so a saved change survives the next page load. - The inline script runs before the shell mounts and sets
--dsw-user-bg-frameand--dsw-user-bg-heroondocument.body. The frame slot also aliases--dsw-alias-bg-baseso every existing reader (AppFrame.module.css, HeroShell.module.css, etc.) inherits the user's value without a single source-file edit.
Known Limitations and Deferred Work
- No HMR re-apply for the boot script. The Host reads the settings section at request time and injects the script once per HTML response; the client half subscribes through
ctx.settingsScope, so the visible background flips after a save without a reload, but the pre-shell paint can only refresh on the next navigation. A live-subscription hook onwebServer.tapIndexwould close the gap. - The
filesource stores the data URL in the durable section. The settings file grows linearly with every upload and is read fully into memory at boot. A future split could store uploads as attachments (via thedsh-attachmentservice) and persist only an attachment reference. - Remote URLs are not fetched at validation time. A malformed URL passes the regex gate but may still fail to load in the browser; CSP, CORS, and broken links surface only when the page actually paints.
- SVG uploads are accepted but not sanitized. A user-supplied SVG can reference external resources or scripts that the same browser tab will then load. A sanitizer pass on the upload path is the right place to plug it.
- Two
settings.general.itemregistrations share one namespace. A future split intosettings.section(one card per slot) would let the user reorder them; today both rows live next to the Appearance row atorder: 20andorder: 21.
Architecture
This package ships both the Node half and the browser half in one tarball:
- Node half (
lib/index.js) — Cordis plugin entry. Registers theui-bgsettings namespace on the host context and taps the index-html response to inject the pre-shell paint script. - Browser half (
lib/client.js) — Self-contained module factory. Renders the two settings cards through the framework'ssettings.general.itemslot, validates writes, and re-renders on revision bumps. - Bundle patch (
cordis.patch.yml) — Declares the plugin id and import target sodsh plugin addreconciles it into the profile's bundle stack.
The dual-face contract (./ for Node, ./client for browser) follows the DSH bundle convention; the Loader and the browser shell each pull the half they need.
License
MIT
