DeepSeek Harness Plugin

jiangnanquan/dsh-ux

Stars ★ 10 Category UI Enhancements Added 2026-08-14

Solarized light theme, compact layout, think/tool-chain collapse capsules, and balance, session cost, and usage dashboards for the DSH web UI.

Install

# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)

dsh plugin --profile web add github:jiangnanquan/dsh-ux

Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).

README

中文 | English

A Web UI enhancement plugin for DeepSeek Harness (DSH), personally built and released as free open source by Jiangnan Quan (江南泉). It adds a dedicated "界面增强" (Interface Enhancements) page to Settings where every toggle applies instantly: 25 terminal-aesthetics themes, text ink, CJK-first typography, compact layout, collapsible capsules, account balance/usage, and official peak/off-peak pricing hints.

dsh-uxexo

Features

Category Description
Settings page Its own page in the settings panel's left rail ("界面增强"): every switch applies instantly, no restart
Themes 25 palettes from Jiangnan Quan's Terminal Aesthetics trilogy: classic CRT, paper-light, modern duals, and AI brands; dark themes set color-scheme: dark automatically
Text ink Deep ink (default, recommended) / follow palette / pure black; changes text tokens only, on light themes
Typography CJK-first 0.5-narrow monospace schemes (Sarasa Mono/Term SC, Noto Sans Mono CJK, Maple Mono…); weight defaults to SemiBold (recommended)
Layout Wider message column, tighter spacing, aligned composer card and / @ candidate menu, rubber-band scrolling disabled
Collapsible capsules 0.1.x: aggregates reasoning into Think ×N and tool calls into an A → B → C chain. 0.2.0: the official process-group folding owns visibility; the plugin attaches a capsule summary below the visible "Completed, took X" row and clicks toggle the official disclosure; long chains wrap by available width
Balance and usage Shows the official account balance, current-session token cost, and a 30-day chart below the composer; official API/account runs also get a peak / off-peak badge, and clicking it explains the official rules and the next switch
Accent The send/stop button and the sidebar "New session" button both take the theme's third anchor (accent); on-accent text picks black or white per palette

Long tool chains reflow with the message column while preserving a complete, independent capsule around each visual line:

Each wrapped tool-chain line uses its own capsule

If dsh-uxexo makes your interface a little nicer, a Star on the repository is the best way to support it.

Themes: 25 palettes from Jiangnan Quan's Terminal Aesthetics trilogy

Pick one from Appearance → Theme in Settings. Entries are grouped by origin and named English · Chinese; the English half keeps each palette's official name and variant (including the accent in Rosé), while the Chinese half is a short translation.

Group Palettes
Official Official · 官方配色
Classic CRT (part 1) P1 Green Phosphor · 绿磷光, P3 Amber Phosphor · 琥珀光, Matrix Rain · 代码雨, Cyberpunk 2077 · 夜之城, Radar Red · 雷达血红, Weyland Corp · 维兰重工, NERV MAGI · 三贤者
Paper light (part 2) P4 White · 纸白, Everforest Light · 绿色森林, Parchment Classic · 羊皮纸, Solarized Light · 暖羊皮纸, Catppuccin Latte · 拿铁
Modern (part 3) Tokyo Night · 东京夜, Tokyo Night Day · 东京白昼, Kanagawa Wave · 神奈川·浪, Kanagawa Lotus · 神奈川·莲, Rosé Pine · 玫瑰松木, Rosé Pine Dawn · 玫瑰晨光, Catppuccin Mocha · 摩卡
AI brands (part 3) Claude Light · 书斋浅, Claude Dark · 书斋深, OpenAI Light · 涌现浅, OpenAI Dark · 涌现深, Gemini Light · 极光浅, Gemini Dark · 极光深

Each palette declares only four anchors — background, text, accent, and state colors — and the rest of the surface / border / hover / on-accent tokens are derived by one shared rule set. That keeps the visual hierarchy consistent across all 25 and makes a new palette a one-line addition. Dark themes set color-scheme: dark and use the palette's own ink; the text-ink control applies to light themes only.

Since 0.2.x the official shared menus use a glass material (--dsw-menu-surface-fill: 58% light / 45% dark plus backdrop blur, backed by an opaque bg-base layer on macOS). The plugin solves the glass fill so that it composites back to the palette's own layer-1, takes over the matching --dsw-alias-menu-group-header-fill (94%), --dsw-alias-menu-icon, and --dsw-alias-label-primary-dimmed, and rebinds the menu's selected/hover fill onto layer-1 (the global hover is base + accent, which is nearly invisible on top of layer-1). Without these, dark palettes would inherit the official light glass and menus would turn into a pale sheet over a dark page.

The same pass takes over every other token added to the official 0.2.x theme table (49 in total: document preview, file diffs, state ladders, masks, tooltip / toast, inverted and bluish labels, trajectory brand color, and so on) with rules isomorphic to the official ones: tertiary state fills are the background mixed with the state color, diff surfaces are lighter mixes of the same, inverted labels take the contrast side of the primary text, and link / deep-diving labels start from accent and are nudged toward black or white until they reach AA. Solarized keeps its hand-tuned block with the official fallbacks (official light values are already valid for a light palette).

The third anchor, accent, is applied to the send/stop button and the sidebar "New session" button: the former always follows it, the latter follows it by default and can be switched to a quiet style (only slightly darker than the sidebar surface) via Appearance → Quiet new-session button.

Every non-Solarized palette meets WCAG AA (4.5:1) for body text on both bg and layer-1; Solarized keeps its hand-tuned token block and relies on the ink setting. Tokyo Night Day's original #3760BF only reached 4.08:1 on layer-1, so its ink was deepened 4% to #2F55B5 (4.74:1).

Text ink: Deep ink (default) / Follow palette / Pure black

Solarized's default body color is base01 #586e75 (a cool grey-green), which looks "veiled" on a warm cream background: 4.99:1 on #fdf6e3, and only 4.39:1 on layer-1 #eee8d5 (below WCAG AA's 4.5). This control changes text tokens only — never backgrounds or brand colors:

Setting Effect
Deep ink (default, recommended) Warm blue-black #002b36, one step deeper than most light palettes' own ink; on Solarized it lifts layer-1 body contrast from 4.39:1 to 12.2:1
Follow palette Inherits the palette's own ink (Solarized's cool grey-green above), closest to the original design
Pure black #000000, 19.5:1; clearer in bright light or for low vision, but heavy for long-form body text

It only applies to light themes: dark palettes carry their own ink, and the on-accent text token (--dsw-alias-label-primary-foreground) is left untouched.

Typography: CJK-first and 0.5-narrow Latin

Based on Terminal Typography Philosophy: CJK-First and the 0.5 Narrow Monospace Practice (measured data below):

A Han character is an uncompromisable 1.0 square. For "2 Latin characters = 1 Han character" to hold exactly, the Latin advance must be narrowed to 0.5. Latin-first programming fonts (Menlo, JetBrains Mono, Fira Code, Maple Mono…) sit at 0.6–0.67, which stretches the Han container to 1.2 — Han glyphs are either widened by 20% (squat, counters collapsed) or surrounded by dead space.

Measured with CoreText on this machine (unitsPerEm=1000, glyph advance widths):

Family Latin a Han 中 Ratio — → ★
Sarasa Mono SC 0.500 1.000 2.000 1.000 1.000 1.000
Sarasa Term SC 0.500 1.000 2.000 0.500 0.500 0.500
Noto Sans Mono CJK SC 0.500 1.000 2.000 0.894 1.000 1.000
Sarasa Gothic / UI SC 0.562 1.000 1.78 1.000 0.954 1.045
Maple Mono NF CN 0.600 1.200 2.000 0.600 0.600 —
Maple Mono (non-CN) 0.600 no glyphs — 0.600 0.600 —

Sarasa Gothic is built exactly on that philosophy: Iosevka Latin (a strict 0.5) plus Source Han Sans Han glyphs (1.0), with baselines, x-height and weight curves aligned at the pixel level. The only difference between Mono and Term is the East Asian ambiguous-width set (—……→★■●): Mono keeps them full-width (best for reading Markdown and Chinese punctuation), Term squeezes them to half-width (best for dense grids and cursor movement). Maple Mono NF CN is the very case the article warns about — its Han glyphs are stretched to 1.2.

Available schemes in the settings page:

Scheme Notes
Auto (recommended) Tries, in order: Sarasa Mono SC → Sarasa Term SC → Noto Mono CJK → Maple Mono; falls back to the official stack when none is installed
Sarasa Mono SC 0.5 narrow Latin, full-width ambiguous characters
Sarasa Term SC 0.5 narrow Latin, half-width ambiguous characters
Noto Mono CJK Noto Sans Mono CJK SC, also 0.5/1.0
Maple Mono Legacy look (Latin 0.6 / Han 1.2), kept for compatibility
Off Leaves the official font stack untouched

Installed families are probed live and shown on the page (unavailable schemes are greyed out; install the font and come back — no restart). A separate weight switch offers Standard and SemiBold; the default is SemiBold (recommended), which lifts body text to 600 and emphasis to 700 using the family's real vector SemiBold (the article's sweet spot). Both modes set font-synthesis: none — the CSS equivalent of the article's "no algorithmic fake bold", which smears counters and glues strokes together.

The page also carries an alignment self-test block (the article's §5 text, re-padded so the columns line up): line 1's 40 half-width characters must be exactly as wide as line 2's 20 Han characters, and the box must render as a rectangle. If they do, the scheme is right.

Rationale, measurements and how to add another family: docs/字体方案-中文优先与0.5窄体.md (Chinese).

Peak / off-peak hints (official API / account)

A small badge appears after the "本轮 ¥x" (current-session cost) cell below the composer. It only shows when the provider actually used by the latest request is deepseek-official or deepseek-account:

State Hours (Beijing time) Display
Peak Mon–Fri (excluding Chinese public holidays), 09:00–12:00 and 14:00–18:00 Amber "峰" (peak), billed at peak rates
Off-peak All other hours, including weekends and public holidays all day Green "谷" (off-peak), billed at half price

The rule follows DeepSeek's official pricing update of 2026-08-23 and the public-holiday clarification of 2026-09-19:

  • Peak applies only on workdays (Mon–Fri, excluding Chinese public holidays) at 09:00–12:00 and 14:00–18:00;
  • Saturdays and Sundays are off-peak all day; make-up work weekends (调休上班) still count as weekends;
  • Chinese public holidays are off-peak all day; off-peak rates are half of peak rates.

Holiday calendar fetched automatically every year

China's holiday schedule is published by the State Council once a year, and the plugin does not require a yearly code update:

  • the host half fetches the machine-readable calendar for the current and next year on demand (from holiday-cn, maintained from the official notice, which is kept in the JSON), parses the isOffDay dates, and caches the result;
  • cache lives in ~/.dsh/dsh-uxexo/calendar-<year>.json; past years are immutable, while the current and next year refresh every 7 days, and the client syncs from the host every 6 hours;
  • if next year's schedule is not published yet (the source returns an empty list), it is not mistaken for "no holidays" — the workday rule is used until the data appears, then it is adopted automatically;
  • when offline, the built-in 2026 table is used as a fallback and never breaks the badge or cost stats;
  • year rollover is therefore automatic: once the 2027 schedule is published, the badge and cost basis switch by themselves with no code change.

Click the badge to open a rule popover: current state and reason (weekend / public holiday / non-peak workday), the exact next switch time with countdown, the official definition, and a source link. Hovering still shows the countdown, and the title of both the Send and model selector buttons carries "官方 API 峰时计费 / 空闲半价" (official API peak / off-peak billing). The state is recomputed every 30 seconds, so crossing 12:00, 18:00, a weekend or a holiday boundary does not wait for the next usage poll.

The criterion is request/header.config.provider in the session log, not "the balance endpoint works" — balance uses an API key, and you may well have switched to scnet or opencode2dsh. In that case no badge is shown rather than a misleading one.

Settings (instant toggles)

Open Settings → 界面增强 (Interface Enhancements). That page holds every switch the plugin owns:

Switch What turning it off does
Enable dsh-uxexo enhancements Master switch. The whole layer is removed immediately and the official look is restored; per-feature choices are remembered and come back when you re-enable it
Theme Selecting "Official" returns to the official palette; the default is Solarized; the 25 palettes and their naming are described above
Quiet new-session button Off by default (the button follows the accent); when on, the sidebar "New session" button becomes only slightly darker than the sidebar surface
Font scheme / weight Back to the official font stack and weights; setting the scheme to "Off" disables just the typography layer; the default weight is SemiBold (recommended)
Text ink Three-way choice: Deep ink (default, recommended), Follow palette, Pure black; affects light-theme text only, never backgrounds or brand colors
Compact layout tweaks Message width, spacing, and composer-card alignment return to the official geometry
Collapsible capsules Reasoning and tool calls expand inline again, and the injected capsule nodes are removed from the DOM
Balance and usage The balance/current-cost row, peak badge, and chart disappear, and the balance endpoint is no longer polled

Everything applies instantly; nothing needs a restart. Switches are projected onto data-dsh-ux-* attributes on <html> (the CSS gates) and persisted in a cookie so the host half can restore them before the first paint (otherwise the official palette would flash for one frame).

The only restart case is updating the plugin itself: the client half is replaced by a page reload or client hot reload, while the host half (the /enh/* endpoints and the first-paint injection) is loaded by the dsh process at startup and cannot be swapped live. The settings page compares the two builds and, when they differ, says so plainly ("restart DeepSeek Harness to take effect") and offers a re-check button. That is what GET /enh/ping is for.

Installation

Prerequisite: DSH is already running through its profile mechanism (dsh web).

dsh plugin --profile web add github:jiangnanquan/dsh-uxexo#main

The package declares both dsh.bundle.patch and dsh.client. dsh plugin add automatically adds the plugin to the profile bundle list. Restart DSH to activate it; no manual configuration edits are required. Running the installation command again is safe and idempotent. To update, run dsh plugin --profile web update dsh-uxexo.

The official desktop app does not use this command: the desktop profile is owned exclusively by the Electron application and dsh plugin --profile desktop … is rejected outright. See INSTALL.md for the desktop path.

Hand It to Your AI Agent

If you prefer not to run the commands manually, paste the following prompt into any AI coding agent (Claude Code, DSH, Gemini CLI, and others) and ask it to follow INSTALL.md:

Follow the instructions in https://github.com/jiangnanquan/dsh-uxexo/blob/main/INSTALL.md to install and verify the dsh-uxexo plugin on my machine using the web profile. Run the documented health checks and report the results. If any step fails, follow the rollback instructions and explain the cause.

Requirements and Notes

  • Font: installing Sarasa Gothic (Sarasa-SuperTTC, or the individual Sarasa Term/Mono SC weights) is recommended — it is the one free family that satisfies both "0.5 narrow Latin" and "1.0 square Han" with aligned weight curves. Without it, Noto Sans Mono CJK SC also measures 0.5/1.0 but ships only Regular/Bold, and Maple Mono (Latin 0.6, Han 1.2) remains available as the legacy look. When none is installed the typography layer falls back to the official stack without affecting anything else.
  • Balance, current-session cost, usage chart, and peak hints: these features call the official DeepSeek API and require DEEPSEEK_API_KEY to be configured in DSH. If credentials are unavailable, the fields show an unavailable or failed state without affecting the other features.
  • The pricing table is hard-coded in pricingFor / PRICE_TABLES inside lib/index.js: peak applies only on workdays at 09:00–12:00 and 14:00–18:00 (Beijing time), while weekends and Chinese public holidays are off-peak all day (half of peak rates). Historical cost is split at 2026-08-17 (peak/off-peak introduced), 2026-08-23 (weekends fully off-peak) and 2026-09-10 12:00 (Flash price cut).
  • The Chinese public-holiday schedule is no longer hard-coded per year: lib/calendar.js fetches, parses and caches it automatically, so year rollover needs no manual work. lib/index.js and lib/client.js each keep a built-in 2026 table purely as an offline/first-paint fallback (the smoke test cross-checks them). DeepSeek price changes are still event-driven and require a manual table update.

Uninstallation

dsh plugin --profile web remove dsh-uxexo

License

MIT

Content from the project README on GitHub ↗

Links

More in this category

View the whole category →

Community comments

Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.