Skip to content

dsh-client-ui-bg

Verified

@ganrunlin-dsh/dsh-client-ui-bg Β· v0.1.1 Β· MIT Β· Web UI

User-customizable web background: local file or remote URL, applied independently to AppFrame and the welcome-screen hero

Install

dsh plugin add @ganrunlin-dsh/dsh-client-ui-bg

Confirm the layer applied with dsh --profile default --dump-config β€” see the install guide.

Source

Published to npm without a public repository. Inspect the package contents before installing.

Creators

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-bg

dsh 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 as peerDependencies, 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 #1f2933 as soon as the regex validates; a URL input commits when the value starts with http:// or https://; a file input commits when the chosen file is ≀ 4 MB and matches one of image/png, image/jpeg, image/gif, image/svg+xml. Other shapes surface an inline error in the row and refuse the write at the schema's validate callback.
  • Each write goes through ctx.settingsScope.bind<BgSettings>(...), fences on the namespace revision, and persists to ~/.dsh/profiles/web/settings.yaml. The Host-side apply() 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-frame and --dsw-user-bg-hero on document.body. The frame slot also aliases --dsw-alias-bg-base so 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 on webServer.tapIndex would close the gap.
  • The file source 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 the dsh-attachment service) 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.item registrations share one namespace. A future split into settings.section (one card per slot) would let the user reorder them; today both rows live next to the Appearance row at order: 20 and order: 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 the ui-bg settings 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's settings.general.item slot, validates writes, and re-renders on revision bumps.
  • Bundle patch (cordis.patch.yml) β€” Declares the plugin id and import target so dsh plugin add reconciles 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