dsh-matugen

August 16, 2026 · View on GitHub

A small, reversible palette bridge from DankMaterialShell / Matugen to the DeepSeek Harness Web theme runtime.

DMS atomically replaces dms-colors.json

DSH Host opens one bounded file descriptor

validate → Material semantic roles → --dsw-* light/dark tokens

GET /dsh-matugen/palette

token SHA-256 revision + whole-snapshot ETag

DSH browser verifies the token digest and validates snapshot metadata

ctx.theme.overrideTokens("dsh-matugen", tokens)

theme/change → existing UI repaints

No edit to the DSH dist, no HMR requirement, no second daemon, and no writes back into DMS or Matugen state. The only DOM compatibility layer is the bounded optional data-color sheet described below; the semantic DSH bridge remains a ThemeRuntime override.

Semantic theme bridge v2

Bridge v2 treats DMS/Matugen as the semantic color authority rather than as a small accent patch. It overrides DSH alias/specific tokens, never the stock --dsw-static-* palette.

The direct coverage includes:

Material familyRepresentative DSH consumers
background / surface_container_low / surface_container / surface_container_high / surface_container_highestpage, layer surfaces, overlay, sidebar/rail, composer, menus, bubbles, code surfaces
primary / primary_container / on_primaryactive Chat/Trajectory accent, business state, send/primary buttons, branded text, selected accent surfaces
secondary / secondary_containerghost-active controls, selectors, secondary selected surfaces, sidebar active surface
tertiaryDSH business-tertiary semantic accent
on_surface / on_surface_variant / outline / outline_variantprimary/secondary/caption text and borders
inverse_surface / inverse_on_surfacetooltip/toast and inverted foreground semantics
errorDSH error state family
optional dank16.color2DSH success primary
optional dank16.color3DSH warning label + primary

DSH state-layer colors that do not have one-to-one Material scheme roles are derived in the bridge. Hover/active layers use either Material-style alpha state layers or an 8% foreground mix over the semantic container, so those interactions track the wallpaper without creating a second static palette.

Newer Material families prefer their exact DMS role. To remain compatible with DMS palettes that expose the original bridge-v1 role floor, v2 has bounded semantic fallbacks, for example:

surface_container_highest → surface_container_high
primary_container          → primary
secondary                  → primary
secondary_container        → surface_container_high
tertiary                   → secondary → primary
inverse_surface            → surface_container_high

The bridge payload itself is protocol version 2 because the required DSH token corpus expanded. A v1 browser/host pair is deliberately not treated as wire-compatible with v2. Every ThemeRuntime override contains both light and dark values; the browser recomputes SHA-256 over the canonical token layer before using revision as content identity. SubtleCrypto is preferred, while ordinary HTTP origins use the dependency-free SHA-256 fallback.

HCT extended colors for dsh-context

dsh-context uses six fixed colors as data category identities, not as UI prominence roles: system, tool schemas, user, injected context, assistant, and tool results. Mapping those categories onto Material primary/secondary/tertiary would make their identity drift with every wallpaper.

dsh-matugen therefore treats the six dsh-context 0.10.x seeds as Material-style extended colors. The Host uses Google's official @material/material-color-utilities HCT implementation to keep each seed's hue/chroma intent while selecting tone 40 for light presentation and tone 80 for dark presentation. Material does not prescribe 40/80 as a chart-series rule; those are this bridge's bounded role-like tone choices.

dsh-context category seed

HCT(seed hue, seed chroma, tone 40 / 80)

light/dark extended-color pair

--dsh-matugen-context-{category}

seed-guarded dsh-context data-viz compatibility selectors

The browser follows DSH's own resolved theme marker, body[data-ds-dark-theme], so a DSH light/dark/system preference change swaps category tone without changing category hue identity. The compatibility sheet is scoped to .lc-root / .lc-modal-card and only overrides known category marks whose inline color still matches the original dsh-context seed. It does not recolor the turn strip. If dsh-context changes a category seed or markup, the affected rule stops matching instead of silently assigning the old identity to a new category.

This extended-color payload is deliberately outside the digest-verified ThemeRuntime --dsw-* token layer. It is same-origin presentation metadata and is validated independently by the browser; the core semantic bridge remains wire-compatible v2.

Boundary

The semantic bridge only controls DSH semantic theme variables. It intentionally does not rewrite --dsw-static-deepseek-*, --dsw-static-blue-*, or other stock scales. A component that directly pins one of those static variables or a literal blue still needs a component-side migration to an alias token. Examples in upstream DSH include the ongoing StateDot, the Chat turn-status shimmer, and reference chip literal colors. Those are presentation/theme-seam follow-ups, not reasons to make the semantic bridge mutate the static palette.

The dsh-context category adapter above is an explicit exception for a third-party data-visualisation surface: it owns no DSH semantic token and is removable if that plugin later exposes theme-aware extended-color hooks of its own.

DMS source

The Host reads by default:

~/.cache/DankMaterialShell/dms-colors.json

Override it through the Host Cordis row or environment:

export DSH_MATUGEN_PALETTE=/path/to/dms-colors.json

Environment and explicit paths use the same ~/relative-path normalization. The Host opens the selected path once, requires a regular file, rejects a file larger than maxPaletteBytes before JSON parsing, and reads at most one byte past that bound through the same descriptor. DMS atomic replacement therefore produces either a complete old snapshot or a complete new snapshot.

Browser transport

The cross-face values are package-fixed:

route   = /dsh-matugen/palette
poll    = 1000 ms
source  = dsh-matugen

DSH's Web boot graph does not serialize a Host Loader row's config into its browser fiber, so making these values look configurable would create a false Host/Client contract. palettePath and maxPaletteBytes remain Host-only.

After the first successful snapshot the browser sends If-None-Match using the whole-response snapshotRevision; an unchanged normalized snapshot receives 304. The separate revision remains the browser-verified SHA-256 identity of the ThemeRuntime token layer. Transient failures keep the last good override layer. Repeated diagnostics are rate-limited in the browser console and a later successful sync emits one recovery message.

The Client effect checks its lifecycle after every asynchronous boundary. If a response settles after the plugin was disposed, it cannot install an orphan ThemeRuntime layer; if disposal races the synchronous install, the just-created layer is immediately disposed. The optional context-category style tag is owned by the same effect and removed on disposal.

DSH browser artifact

DSH Web loads a plugin's exports["./client"] as a lazy CJS factory registered through:

window.__ModuleLoader__.load({ id: "dsh-matugen", factory })

This repository builds that artifact with a small repo-local Node script:

npm run build

The browser artifact has zero npm runtime imports. The builder admits only the single local ./core.js static import used by src/client.js, rejects any additional static import and every dynamic import(...), strips the known ESM module surface, emits lib/client.js, and fails if the generated browser bundle contains either require() or import(...).

The Host package has one runtime dependency: @material/material-color-utilities@0.4.0, used only to compile the fixed category seeds into HCT light/dark pairs. It is not bundled into or required by the browser artifact. Git installs run the browser builder through prepare.

DSH composition

Once the package is resolvable by DSH and lib/client.js exists, insert the Host row into the Web profile. During current local dogfood the package is installed as a plain profile dependency, so use the checked-in examples/cordis.patch.yml insert layer:

- insert:
    - id: dsh-matugen
      name: dsh-matugen
      config:
        palettePath: !!js process.env.DSH_MATUGEN_PALETTE || process.env.HOME + '/.cache/DankMaterialShell/dms-colors.json'

The Host half injects ctx.webServer; the browser half declares the DSH theme plugin as its client dependency and collaborates only through ctx.theme. Both halves are namespace plugins (inject + apply) with no default export, so Cordis Loader preserves their injection metadata.

Verification

npm install --ignore-scripts
npm run check

The ordinary gate covers semantic role coverage, derived state layers, legacy-role fallbacks, protocol validation, digest verification with and without SubtleCrypto, HCT category identity/tone generation, seed-guarded dsh-context CSS, bounded reads, conditional GET/HEAD, path non-disclosure, client diagnostics, dynamic-import rejection, built lazy-module geometry, and the post-disposal race.

CI also runs a separate exact @deepseek-ai/dsh@0.1.0-rc.6 seam gate. It boots the real rc.6 Host WebServer and loads the Host plugin through the real Cordis Loader before performing an HTTP request through the bridge; it additionally checks the exact rc.6 theme package's published overrideTokens(source, ThemeTokenOverrides): () => void declaration and its lazy browser artifact. This is an rc.6 seam test, not a claim that CI has booted a full graphical DSH Web session.

The remaining end-to-end dogfood gate is deliberately physical: compose this package into the current DSH Web deployment, change the DMS wallpaper/palette, and observe the real browser/iPad repaint and unload/reload behavior. For v2, physical inspection should specifically confirm that active tabs, caret/focus, primary/send controls, selectors, elevated surfaces, and Trajectory business accents follow the current Material scheme while error/success/warning retain their operational semantics. With dsh-context installed, also switch DSH light/dark appearance and confirm that the six data categories keep their identity while their HCT tone adapts in both the Context tab and /context modal.

Scope

dsh-matugen is the DMS provider + DSH semantic token compiler. dsh-rice consumes the same DSH theme variables while owning layout and presentation independently; it should not need to know which wallpaper produced them.