Installation
npx -y @deepseek-ai/dsh plugin --profile web add github:189-sketch/dsh-custom-logoThis installation command is an unverified starting point generated from the GitHub repository address.
README
Maintainer-authored documentation snapshot.
dsh-custom-logo
Replace the top-left DSH logo (
<BrandWordmark>+<FishLogo>) with your own image or text — directly inside the wordmark, no floating widget.
A standalone DeepSeek Harness (dsh) profile-bundle plugin. Replaces the
hard-coded deepseek harness wordmark with a user-chosen image, text, or
the original logo. The configuration handle (⚙) is inline inside the
wordmark — hover the logo and the handle appears at the right edge; click
it to open the settings panel.
(drop a screenshot in docs/screenshot.png and replace this URL)
Why this plugin
The default DSH web UI shows a fixed deepseek harness wordmark in the
top-left corner of the sidebar. Some users want their own brand there
(their company name, a project codename, a logo), and there's no built-in
option. This plugin adds that option without:
- modifying DSH source
- changing the "New session" click target on the wordmark button
- obstructing the sidebar-collapsed rail (the toggle button is fully visible)
- using a floating widget that hangs around the viewport
The replacement is scoped to image / text modes — in default mode the
original wordmark renders untouched. The plugin is invisible until you
configure it.
Features
- Three modes: Default (no change) / Image (≤ 256 KB upload) / Text
- Inline settings handle — hover the wordmark, ⚙ appears at the right edge, click to configure
- Alignment for image and text modes: left / center / right
- Font size for text mode: 10-48 px slider, double-click "auto" to fit the original wordmark height
- Color picker for text mode
- localStorage persistence — your choice survives page refresh, no login required
- Sidebar-collapsed safe — when the DSH sidebar is in the rail state, the plugin removes everything so the toggle button is never obstructed
- Settings panel lives in a Shadow DOM so its styles never bleed into the DSH shell
- One-line install / uninstall via
dsh plugin add
Installation
# Local development (links the checkout without copying)
dsh plugin --profile web add link:/path/to/dsh-custom-logo
# From GitHub (once published)
dsh plugin --profile web add git+https://github.com/<you>/dsh-custom-logo.git
# From npm (once published)
dsh plugin --profile web add dsh-custom-logo
Restart dsh web if it was already running. Hard-refresh the browser
(Ctrl+F5) to bypass the client bundle cache.
Usage
- Hover the top-left
deepseek harnesswordmark → a small ⚙ handle fades in at the right edge. - Click ⚙ → the settings panel opens.
- Choose a mode:
- Default — close the panel; the original DSH logo returns
- Image — upload a PNG / SVG / JPG (≤ 256 KB), pick alignment
- Text — type your brand string, pick a color, font size, and alignment
- The change is instant and saved to
localStorageautomatically. - Fold the sidebar to see the rail; your replacement does not obstruct the toggle button.
To reset, click Reset in the settings panel.
How it works
DSH renders the wordmark as a <button class="brand"> (see
packages/client/ui-sidebar/src/client/SidebarRoot.tsx). The plugin
appends a single <span data-clogo-handle> to that button. CSS hides
the handle by default; JS hover listeners toggle inline opacity: 0 ↔ 1.
When the user picks Image or Text mode, the plugin also appends a
<span data-clogo-slot> next to the BrandWordmark <svg>. A CSS
general-sibling rule hides the original SVG when the slot exists
([data-clogo-slot] ~ svg { display: none }).
Both elements are owned by module-level singletons in the client
bundle — paint() does not recreate them on every store change. This
survives DSH re-rendering the sidebar (a real concern with React-based
DSH) without losing hover state mid-interaction.
Sidebar-collapsed detection
paint() measures [data-clogo-row] (a marker set on DSH's .brand
button on first paint). If the row is narrower than 90 px, the plugin
treats the sidebar as collapsed and removes everything — DSH has
unmounted the entire .brand button in the rail state, so the
original toggle button has the space it needs.
Why not DSH's ctx.styles.insert?
The plugin uses tsdown's CSS Modules inliner (not DSH's
ctx.styles.insert) because:
ctx.stylesis a DSH client builtin, but its TS signature lives in DSH's own type declarations; standalone plugins without monorepo type access can use it but lose type safety- CSS Modules lets the plugin ship a single self-contained bundle that DSH's module table loads without a separate runtime API call
- The file is named
*.module.cssso tsdown's inliner picks it up — plain.cssrequires@tsdown/css(a separate dependency)
Layout
dsh-custom-logo/
├── package.json # dsh.bundle.patch + dsh.client declarations
├── cordis.patch.yml # the layer applied to the user's profile
├── tsconfig.json # self-contained (no monorepo paths)
├── tsdown.config.ts # inlines the DSH clientBundle preset
├── README.md
├── LICENSE
├── src/
│ ├── plugin/
│ │ ├── index.ts # Host half: typed `apply(ctx: Context)`
│ │ └── invariant.ts # dsh-invariants companion (no-op installer)
│ └── client/
│ ├── index.ts # Browser half: paint, store, settings modal
│ └── logo.module.css # CSS Modules — inlined into lib/client.js
└── lib/ # build output (`pnpm run build`)
├── index.js # ESM — Host half
├── invariant.js # ESM — dsh-invariants companion
├── client.js # CJS — browser bundle (window.__ModuleLoader__)
└── types/
├── plugin/{index,invariant}.d.ts
└── client/index.d.ts
Development
# Install deps
pnpm install
# Type-check + bundle (tsc + tsdown)
pnpm run build
# Watch mode
pnpm run watch
After pnpm run build, the bundle at lib/client.js is what DSH loads
through the module table.
Configuration
The runtime choice is persisted in window.localStorage under
dsh.custom-logo.v1. The state shape:
{
mode: 'default' | 'image' | 'text',
imageDataUrl: string, // data URL; only used in image mode
text: string, // brand string; only used in text mode
color: string, // hex color; only used in text mode
align: 'left' | 'center' | 'right',
fontSize: number | null // null = auto-fit the original wordmark height
}
This plugin is a workspace bundle — it does NOT receive a config
argument to apply() (that channel is reserved for dynamic plugins
through cordis-client-runner). Deployment defaults are encoded in
src/client/index.ts.
Uninstall
dsh plugin --profile web remove dsh-custom-logo
Then restart dsh web. The plugin removes its localStorage entry only
on user-driven reset — it survives uninstall to ease re-install.
Known limitations
- DSH 内部 logo DOM 变化:DSH 重渲染 sidebar 时
<button class="brand">可能被 unmount + 重新 mount。paint()通过 module-level singleton 复用 handle 元素并重新 attach,但首次延迟后用户可能看到一秒的不可见。 - localStorage scope:状态按浏览器 profile 隔离,多 profile / 多浏览器不共享。
- Image size:客户端
<input type="file">限制 256 KB(用 FileReader 转 data URL 内嵌)。 - No SSR / no Node.js runtime:仅浏览器侧运行;Cordis 宿主侧只是 no-op。
- No file watcher in production:修改
src/后必须pnpm run build一次;watch仅用于开发。
License
MIT
Repository information
- Language
- TypeScript
- License
- MIT
- Latest release
- v0.1.0
- Last updated
- Aug 17, 2026, 8:08 AM
Install deliberately
Review source code, permissions, lifecycle hooks, dependencies and network access. Test untrusted plugins in an isolated environment.