Back to home

RealHacker

dsh-theme-colorizer

A Deepseek Harness plugin that gives its UI color themes

Stars
0
Language
TypeScript
Created
Aug 15, 2026
Updated
Aug 15, 2026

Introduction

dsh-theme-colorizer

Color theme

A DeepSeek Harness (DSH) plugin that adds customizable color themes to the DeepSeek Web UI. The user selects a color theme on the Settings page, and the chosen theme's --dsw-alias-* CSS token overrides are applied on top of the active light/dark base palette.

Features

  • 5 color themes: Ocean Blue, Forest Green, Sunset Orange, Royal Purple, Monochrome
  • Settings page integration: a "Color Theme" row appears in the General section of the Settings panel
  • Persisted preference: the user's choice is stored in the DSH user-settings document and survives restarts
  • Composes with the built-in Appearance row: the color theme is independent of the light/dark/system preference — both layers compose through the theme service's override stacking

How it works

The DSH plugin loading model

A DSH plugin is a Cordis plugin module. It exports an apply(ctx) function that the Cordis loader calls with a context (ctx). Every registration the plugin makes — settings namespaces, theme overrides, slot registrations — is an effect on that context, so unloading the plugin automatically tears down its contributions.

This plugin has two halves because it contributes to both the Host (Node.js server) and the Client (browser) sides of the DSH web application:

Host side (src/index.ts)

The Host half runs in the Node.js process. It registers a settings namespace (theme-colorizer) with the DSH settings service. This namespace stores the user's selected color theme id in the user-settings document (e.g., settings.yml). The registration is an effect on the plugin's fiber, so disposing the plugin removes the namespace.

When loaded: at server startup, when the Loader reads cordis.yml and mounts this plugin entry. The Host half injects ['settings'], so it waits until the settings service exists before running.

Client side (src/client/index.ts)

The Client half runs in the browser. It:

  1. Binds the settings scope — connects to the Host's theme-colorizer namespace through the ctx.settingsScope transport. It reads the stored color theme id and subscribes to changes.

  2. Applies token overrides — calls ctx.theme.overrideTokens('dsh-theme-colorizer', tokens) to overlay the selected theme's --dsw-alias-* CSS custom properties on top of the active base palette. The theme service composes override layers in stacking order; later layers win per-token. The plugin's layer is identified by the source string 'dsh-theme-colorizer', so re-calling with the same source replaces the layer atomically.

  3. Registers a settings row — injects into the settings.general.item slot and registers a ThemeColorRow React component that renders the five color-theme swatches. The component receives the current selection and a setColorTheme callback through its inject face.

When loaded: when the browser's module loader constructs the window.__DSH_BOOT__ plugin graph. The dsh.client manifest in package.json declares this is a web plugin with immediately: false, so it loads during the normal plugin graph initialization (not in the stage-one prefetch tier).

Settings row rendering

The ThemeColorRow component renders inside the General section of the Settings panel. It uses the DSH slot system:

  • The ui-settings package declares the settings.general.item slot type
  • The ui-settings-general package renders the General section and its item slots
  • This plugin registers into that slot with id: 'theme-colorizer' and order: 20 (placing it after the built-in Appearance row at order 10)

Theme token override flow

User picks "Forest Green" in Settings
  → ThemeColorRow calls setColorTheme('forest-green')
  → Client plugin writes to settings scope (Host persists it)
  → Client plugin calls ctx.theme.overrideTokens('dsh-theme-colorizer', forestTokens)
  → ThemeRuntime publishes theme/change event
  → ui-layout's ThemePresenter reads the new snapshot
  → CSS variables on <body> update
  → All components re-render with new colors

Installation

Prerequisites

  • A working DeepSeek Harness installation (the deepseek-harness repository checkout)
  • The DSH web app is built and running (pnpm run dev:web or dsh web)

Step 1: Place the plugin in the packages directory

Copy or symlink the plugin into the packages/client/ directory of the deepseek-harness repository:

# From the deepseek-harness root:
Copy-Item -Recurse E:\Deepseek\Workspace\plugins\dsh-theme-colorizer packages\client\dsh-theme-colorizer

Or, for development with live edits:

# Symlink so changes to the workspace copy are reflected immediately:
New-Item -ItemType Junction -Path packages\client\dsh-theme-colorizer -Target E:\Deepseek\Workspace\plugins\dsh-theme-colorizer

Step 2: Register the plugin as a TypeScript project

The DSH build is two-phase: tsc -b first compiles every package and emits lib/types/*.js, then tsdown bundles each package from those emitted files. tsc -b only compiles packages listed as project references of an aggregate, so the new package must be added to tsconfig.client.json:

Edit tsconfig.client.json at the repo root and add one line to the references array (alphabetical order, e.g. after ./packages/client/ui-theme):

{ "path": "./packages/client/dsh-theme-colorizer" },

The package's own tsconfig.json already extends tsconfig.base.client.json (which sets composite: true) and references its dependencies (locale, runtime, ui-slots, ui-settings, ui-theme, settings, invariants, cordis), so it is a valid project-reference leaf.

Step 3: Add the plugin to the web-app composition

Edit packages/bundle/web-app/cordis.patch.yml and add the plugin entry in the insert block, under the dsh.client rows section (near the other ui-* entries):

    # Add after the ui-theme entry:
    - id: theme-colorizer
      name: 'dsh-theme-colorizer'

Step 4: Add the package as a dependency

Edit packages/bundle/web-app/package.json and add "dsh-theme-colorizer": "workspace:^" to both dependencies and devDependencies (or only dependencies if the package is not built from source).

Step 5: Install and build

cd deepseek-harness
pnpm install
pnpm run build:lib:client
pnpm run build:web

Do not use pnpm run build --filter dsh-theme-colorizer — the package has no standalone build script, and tsdown alone cannot build it (its entry points are the lib/types/*.js files that only the tsc -b phase produces).

  • pnpm run build:lib:client runs tsc -b tsconfig.client.json (compiles the new package → emits lib/types/), then tsdown --env.DSH_BUILD_FACE client (bundles the node-half lib/index.js + lib/invariant.js and the browser half lib/client.js). The workspace tsdown pass auto-discovers the package through the packages/*/* glob — no other build wiring is needed.
  • pnpm run build:web rebuilds the frontend shell. (Plugin bundles are served at runtime from each package's lib/client.js, so this is only needed if the shell itself changed; restarting the server alone is enough in most cases.)

Step 6: Restart the web server

If the web server is already running, restart it:

# Using dsh CLI:
dsh web

# Or during development:
pnpm run dev:web

Verification

  1. Open the Web UI at http://127.0.0.1:3080
  2. Click the Settings icon (gear) in the sidebar
  3. In the General section, you should see the "Color Theme" row with five colored swatches
  4. Click a swatch — the UI colors should update immediately
  5. The selection persists across page reloads (it is stored in the DSH user-settings document)

File structure

dsh-theme-colorizer/
  package.json              # Package manifest with dsh.client metadata
  tsconfig.json             # TypeScript config (extends tsconfig.base.client.json)
  tsdown.config.ts          # Build config using the shared clientBundle preset
  src/
    index.ts                # Host-side entry: registers settings namespace
    invariant.ts            # Companion plugin for invariant checks
    theme-color-settings.ts # Shared settings schema and types
    css-modules.d.ts        # Type declarations for CSS Module imports
    client/
      index.ts              # Client-side entry: applies theme overrides, registers settings row
      color-themes.ts       # Theme definitions (token overrides for each color theme)
      ThemeColorRow.tsx      # React component for the settings row
      ThemeColorRow.module.css # Styles for the settings row
      locales.ts            # i18n strings (zh/en)
  README.md                 # This file

Known Limitations and Deferred Work

  • Color theme does not affect the scrollbar or code-highlighting colors — those are styled by separate stylesheets (scrollbar.css, shiki.css) that use their own token sets. A future version could override those tokens as well.
  • No custom theme authoring — the five themes are built in. A future version could allow users to define custom themes through the settings document or a theme editor.
  • The active theme is not reflected in the initial HTML bootstrap — the first paint before the client plugin tree activates uses the default theme. The built-in theme bootstrap (boot-theme.ts) only handles the light/dark/system preference. A future version could also embed the color-theme tokens in the bootstrap script.