DSH Plugins Marketplace

DSH Plugins

Plugins

/

dsh-user-profile

d

dsh-user-profile

Manifest valid

Edit your own display name and avatar in DeepSeek Harness — shown in the sidebar account chip. | DSH 插件:自定义用户名与头像。

UI (client)hasBundlePatch

dsh-user-profile

Your own name, avatar and brand, inside DeepSeek Harness.

Set a username and avatar in Settings → Profile, or replace the product name and logo in Settings → Brand; both show up immediately in the sidebar.

A local display profile for DeepSeek Harness.

Platform License: MIT topic: dsh-plugin DSH CI GitHub release

中文 · English


Sidebar with a custom brand and a custom profile

Why

The account area at the bottom-left of DSH renders the DeepSeek Platform account — read-only — and degrades to a generic "More" button when nobody is signed in. The top-left corner is a fixed product wordmark. Neither is yours to change.

dsh-user-profile fills both gaps: a name and avatar stored locally and overlaid on the account control, and an optional product name and logo that replace the sidebar brand. The account menu (settings / feedback / sign out) keeps working; only the face changes.

🪪 UsernameUp to 32 characters, any script; leave it empty for the default
🖼️ AvatarPNG / JPG / WebP, center-cropped and downscaled to 192×192
🏷️ BrandProduct name (≤ 32 characters) and a logo for the top-left corner
👀 Live previewSee the result while editing; the sidebar updates on save
🔄 ReversibleReset clears the profile and the brand restores the shipped wordmark
🌐 LocalizedFollows the DSH language setting (中文 / English)
💾 Local onlyNothing uploaded or synced; it lives under your own ~/.dsh
Settings → Profile Settings → Brand

Install

As a bundle (recommended)

The package declares dsh.bundle.patch, so the official CLI also mounts the plugin row:

dsh plugin --profile web add github:xbbkok/dsh-user-profile

Use desktop instead of web on the desktop app, then reopen DSH.

From a local checkout

Append an insert entry to the profile's cordis.patch.yml. The loader uses import(), so the specifier must be a file:// URL — a bare Windows path fails silently:

- insert:
    - id: dsh-user-profile
      name: 'file:///C:/path/to/dsh-user-profile/lib/index.js'

Profile patch locations:

BuildPath
Desktop%USERPROFILE%\.dsh\profiles\desktop\cordis.patch.yml
Web%USERPROFILE%\.dsh\profiles\web\cordis.patch.yml

The row is keyed by id, so a later bundle install overrides the same row instead of mounting a second copy.

Usage

  1. Sidebar account chip → Settings (Ctrl+Alt+,).
  2. Profile: set a name, pick an image, press Save.
  3. Brand: type a product name, upload a logo, press Save.
  4. Reset in either page clears that piece and restores the shipped look.

Storage

%USERPROFILE%\.dsh\plugins-data\dsh-user-profile\profile.json
{
  "version": 2,
  "name": "Ada",
  "avatar": "data:image/png;base64,…",
  "brand": { "name": "ACME Studio", "icon": "data:image/png;base64,…" },
  "updatedAt": "2026-01-01T00:00:00.000Z"
}

heartbeat.txt in the same directory is rewritten on every host mount, so "did it load?" is a fact you can check.

The avatar is a 192×192 PNG data URL (usually 10–40 KB); the brand logo is 96×96. Both are cropped and compressed in the browser — the original file never touches disk.

API

The host half registers two methods on the DSH web server (same origin as the GUI):

MethodPathNotes
GET/user-profile{ ok, profile, storagePath, capabilities }
POST/user-profileBody { name?, avatar?, brand?: { name?, icon? } }; omitted fields keep their value, null clears the image

Validation: names are limited to 32 code points with control characters stripped; avatars and logos accept only PNG/JPEG/WebP data: URLs (SVG is rejected — it is executable markup, not a picture) and a byte cap; writes carrying an Origin that does not match the local host get a 403, so another web page cannot rewrite the profile cross-site.

capabilities.brand tells the client that this host half understands brand; a page served by an older host half shows a "restart DSH" note instead of losing an edit.

How it works

One package, two halves, like the other DSH UI plugins:

  • lib/index.js (host / Node) — owns /user-profile: validation, atomic write (temp file + rename), heartbeat. No third-party runtime dependencies.
  • lib/client.js (browser) — registers two settings sections through ctx.slots.register({ name: 'settings.section', … }), claims the brand slots, and overlays the avatar and name on the account trigger button[data-signed-out].

Deliberate choices:

  • The brand uses the official slots. sidebar.brand.mark and sidebar.brand.name are single slots: registrations at different priorities coexist and the lowest priority renders. The plugin claims both at priority -1 and renders the shipped FishLogo / BrandWordmark whenever nothing custom is set, so an unconfigured install is pixel-identical to stock and clearing the brand restores it with no teardown.
  • No forking of someone else's component. The account button belongs to dsh-client-ui-settings-account; editing that package would be fragile and would be overwritten by updates. The bundle decorates the DOM instead: it records each native child's display, hides it, inserts its own avatar and label, and restores everything when the profile is cleared. A MutationObserver re-applies after React re-renders, and every write is idempotent so the observer cannot feed itself (covered by a test).
  • Failures stay inside. A missing web server, an unwritable directory or a failing route registration produce a warning, never a broken plugin tree.
  • Language follows the app. Dictionaries go through ctx.locale.register('user-profile', { zh, en }) and are read through ctx.locale.bind(), so the page honours DSH's own language setting.
  • No build step. lib/*.js is readable ESM; the window.__ModuleLoader__.load() factory shape is consumed directly by the client module system — no bundler, no postinstall.

Compatibility

  • DSH 0.2.x (@deepseek-ai/dsh >= 0.1.2-rc.1), desktop and web builds.
  • Uses the settings.section slot, the sidebar.brand.mark / sidebar.brand.name slots (priority shadowing), and the data-signed-out attribute of the account trigger; all are stable since 0.1.x.
  • Display only: sign-in, billing and model calls are untouched.

Known limitations

  • It changes the displayed profile and brand, not the DeepSeek Platform account.
  • The profile is per machine; it does not sync across devices.
  • The brand replaces the expanded wordmark; the collapsed rail keeps showing the mark slot, which is where a custom logo also appears.
  • If a future DSH build renames button[data-signed-out], the account-chip overlay silently stops applying (the settings pages keep working).
  • The account-chip overlay is DOM decoration on a component owned by another package, not an official slot.

Development

npm install   # jsdom only, for running the browser half without a GUI
npm test

27 checks: host validation, atomic persistence, the HTTP handler (GET / POST / brand merge and reset / malformed JSON / over-long name / SVG disguise / cross-origin write / wrong method), the 1.0.0 document upgrade, bundle loading, slot registrations with localized labels, brand slots at priority -1 with shipped fallbacks, chip overlay and exact restoration, collapsed rail, observer settling.

Editing lib/client.js hot-swaps in the page; editing lib/index.js needs a DSH restart to re-import.

Uninstall

Remove the dsh-user-profile row from the profile's cordis.patch.yml (or drop it from dsh.profile.bundles), delete %USERPROFILE%\.dsh\plugins-data\dsh-user-profile\, and refresh.

License

MIT © 2026 xbbkok

Comments

Loading…