dsh-theme-plugin
Chinese traditional colors as a DeepSeek Harness theme pack — 48 anchors × light/dark = 96 themes, each writing the full --dsw-* token vocabulary (89 tokens) and passing WCAG AA contrast checks (2208/2208 rows, 0 failures).
📖 中文文档
Install
npx -y @deepseek-ai/dsh plugin --profile web add dsh-theme-plugin@latest
npx -y @deepseek-ai/dsh --profile web
That pulls the prebuilt bundle from npm — no clone, no build step. The web profile is created on first boot at ~/.dsh/profiles/web.
- Verify:
dsh --profile web --dump-config shows a theme-zhongguo row; the browser console logs registered 96/96 themes.
- Update: run the same
add command again (@latest).
- Uninstall:
dsh plugin --profile web remove dsh-theme-plugin
From source (development)
Requirements: Node.js 20+ (engines in package.json; pnpm test runs .ts directly and needs 23.6+) and pnpm.
git clone https://github.com/nevertoday/dsh-theme-plugin
cd dsh-theme-plugin
pnpm install && pnpm build
dsh plugin --profile web add -w .
dsh --profile web
- ①
pnpm install already builds once through its prepare script; the explicit pnpm build is a safety net (and required if you install with --ignore-scripts). It needs tsdown; fallback node scripts/build-esbuild.mjs (esbuild is already a devDependency). The repo ships an .npmrc with auto-install-peers=false — without it pnpm ≥ 9 cannot install: it tries to fetch the @deepseek-ai/* peers (even though all are marked optional), and the latest of dsh-client-runtime depends on a package that was never published to npm, so the install dies with ERR_PNPM_FETCH_404.
- ②
-w is required because the profile directory is a pnpm workspace root. add anchors relative specs like . to the directory you run it from. It links the directory (link: dependency) and auto-appends the package to dsh.profile.bundles; the loader reads lib/client.js straight from your working copy — step ① creates that file, so committing lib/ is not required (committing it is fine too; the cost is 553KB of output plus a 790KB sourcemap showing up as changes after every install).
- Gates, no harness needed:
pnpm check (2208 contrast rows plus invariants) and pnpm test (23 behavioral assertions, including a load-time lock on lib/client.js).
- Update:
git pull && pnpm install && pnpm build, then boot — no re-registration needed.
Usage
Open Settings → Traditional Colors (after "Agent presets" in the nav) and pick a theme — applies immediately. Or share/bookmark a deep link:
http://127.0.0.1:3080/#theme=zhuqing-light # 竹青 light
http://127.0.0.1:3080/#theme=qunqing-dark # 群青 dark
Changing the hash switches themes live. Your pick is remembered per browser (localStorage), not in settings.yaml — it does not follow you across devices.
The panel is themed by the pack itself — its styles reference nothing but --dsw-* tokens, so it doubles as a demo of the theme you are picking.
Design: Paper · Veil · Seal
Chinese painting does not start with color. It starts with preparing the paper, then washes over it, and signs last. These themes are built in the same order, and the three characters map onto three implementation layers.
- 纸 Paper (layer A, ~60% of surface area) — the ground is not "the traditional color, lightened". It is a different material: four families (su juan raw silk, shu xuan sized paper, xue qing violet silk, zhe zhi ochre paper) each with their own lightness and chroma gates. The anchor is left as a trace, chroma clamped hard, so the ground reads as "this particular white (or black) is a little unusual". No two of the 96 themes share a background.
- 帘 Veil (layer B, ~25%) — the sidebar and message bubbles are the anchor color itself, undiluted by paper, on their own chroma gate. You recognize which traditional color you are in by the bubbles, not by the background.
- 印 Seal (layer D, <0.5%) — the primary button is deliberately not the anchor. It is a curated relative of it: green-blue paired with a vermilion seal, gold ground with an azurite one. The most saturated patch on screen is this one and only this one, so a single focus point comes for free. The generator records
sealName / sealRel / sealWhy per theme.
- Ink and empty space (layer C) — text, rules, and secondary surfaces run down one ink ramp (the paper color pushed darker). Every
nb-XX reference from the base stylesheet becomes a tinted neutral N(XX) of the same lightness; hover offsets, elevation steps, borders, and interaction alphas are copied verbatim. Hue changes, relations do not.
- Provenance — the generator distinguishes a named color (a traditional hex used directly) from a fallback (a value derived along OKLab lightness by
ensure()), and records each in degraded. Current roster: bubble-chroma fallback in 18 themes, seal fallback in 10, across 27 themes total. Anchors that fail the AA assertion matrix are dropped whole, with the reason logged.
Anchor distribution across paper families: 素绢 12 · 熟宣 14 · 雪青 15 · 赭纸 7.
Theme roster
48 anchors × light/dark = 96 themes — click to expand
Display names are <name>·亮 / <name>·暗 (e.g. 竹青·暗). Paper families: 素绢 = raw silk, 熟宣 = sized xuan paper, 雪青 = violet-tinted silk, 赭纸 = ochre paper.
| Zhu Qing | 竹青 | #00A86B | 素绢 | 茜红 | zhuqing-light / zhuqing-dark |
| Zhu Hong | 朱红 | #ED5126 | 熟宣 | 赭石 | zhuhong-light / zhuhong-dark |
| Qun Qing | 群青 | #1772B4 | 雪青 | 枫叶红 | qunqing-light / qunqing-dark |
| Teng Huang | 藤黄 | #FFD111 | 赭纸 | 绀青 | tenghuang-light / tenghuang-dark |
| Jiang Zi | 绛紫 | #8E354A | 熟宣 | 洋葱紫 | jiangzi-light / jiangzi-dark |
| Zi Yun | 紫云 | #A020F0 | 雪青 | 蜻蜓红 | ziyun-light / ziyun-dark |
| Mei Hong Se | 玫红色 | #FF007F | 熟宣 | 品红 | meihongse-light / meihongse-dark |
| Dan Shu Hong | 淡曙红 | #EE2746 | 熟宣 | 殷红 | danshuhong-light / danshuhong-dark |
| Gan Qing | 绀青 | #4F84FF | 雪青 | 落霞 | ganqing-light / ganqing-dark |
| Mei Gui Zi | 玫瑰紫 | #BA2F7B | 熟宣 | 高粱红 | meiguizi-light / meiguizi-dark |
| Ying Wu Lü | 鹦鹉绿 | #5BAE23 | 素绢 | 猩红 | yingwulv-light / yingwulv-dark |
| Bo Luo Hong | 菠萝红 | #FC7930 | 熟宣 | 芙蓉红 | boluohong-light / boluohong-dark |
| Fu Pen Zi Hong | 覆盆子红 | #AC1F18 | 熟宣 | 苋菜红 | fupenzihong-light / fupenzihong-dark |
| Cang Bi | 苍碧 | #2A52BE | 雪青 | 猩红 | cangbi-light / cangbi-dark |
| Xiong Huang | 雄黄 | #FF9900 | 赭纸 | 绀青 | xionghuang-light / xionghuang-dark |
| Wei Zi | 魏紫 | #7E1671 | 雪青 | 魏紫·深 | weizi-light / weizi-dark |
| Gan Lan Huang Lü | 橄榄黄绿 | #BEC936 | 素绢 | 魏紫 | ganlanhuanglv-light / ganlanhuanglv-dark |
| Xin He Lü | 新禾绿 | #D2B116 | 赭纸 | 釉蓝 | xinhelv-light / xinhelv-dark |
| Huo Zhuan Hong | 火砖红 | #CD6227 | 熟宣 | 淡可可棕 | huozhuanhong-light / huozhuanhong-dark |
| Xiang Ye Hong | 香叶红 | #F07C82 | 熟宣 | 鹅冠红 | xiangyehong-light / xiangyehong-dark |
| Yan Ying Zi | 烟萦紫 | #8A4B9C | 雪青 | 烟萦紫·深 | yanyingzi-light / yanyingzi-dark |
| Mei Ge | 韎韐 | #A5441B | 熟宣 | 蟹蝥红 | meige-light / meige-dark |
| Li Shou | 綟绶 | #6B8E23 | 素绢 | 暗紫苑红 | lishou-light / lishou-dark |
| Zi Teng Luo | 紫藤萝 | #9B8AE8 | 雪青 | 紫藤萝·深 | zitengluo-light / zitengluo-dark |
| Han Xiu Lü | 汉绣绿 | #2E7D32 | 素绢 | 绛紫 | hanxiulv-light / hanxiulv-dark |
| Jin Zong | 金棕 | #B8860B | 赭纸 | 柏林蓝 | jinzong-light / jinzong-dark |
| An Zi Yuan Hong | 暗紫苑红 | #82202B | 熟宣 | 殷红 | anziyuanhong-light / anziyuanhong-dark |
| Xin Lü | 新绿 | #6CC788 | 素绢 | 茜裙 | xinlv-light / xinlv-dark |
| Ling Meng Hong | 菱锰红 | #D276A3 | 熟宣 | 苋菜紫 | lingmenghong-light / lingmenghong-dark |
| Man Tian Xing Zi | 满天星紫 | #2E317C | 雪青 | 栗紫 | mantianxingzi-light / mantianxingzi-dark |
| Kong Que Lan | 孔雀蓝 | #0EB0C9 | 雪青 | 胭脂红 | kongquelan-light / kongquelan-dark |
| Mei Die Lü | 美蝶绿 | #12AA9C | 素绢 | 枫叶红 | meidielv-light / meidielv-dark |
| Bian Dou Zi | 扁豆紫 | #A35C8F | 雪青 | 扁豆紫·深 | biandouzi-light / biandouzi-dark |
| Qing Fan Lü | 青矾绿 | #2C9678 | 素绢 | 汉绣红 | qingfanlv-light / qingfanlv-dark |
| Bi Luo Chun Lü | 碧螺春绿 | #867018 | 赭纸 | 苍碧 | biluochunlv-light / biluochunlv-dark |
| Gan Lan Shi Lü | 橄榄石绿 | #B2CF87 | 素绢 | 酢酱草红 | ganlanshilv-light / ganlanshilv-dark |
| Fen Tuan Hua Hong | 粉团花红 | #EC9BAD | 熟宣 | 锦葵红 | fentuanhuahong-light / fentuanhuahong-dark |
| He Ye Lü | 荷叶绿 | #1A6840 | 素绢 | 栗紫 | heyelv-light / heyelv-dark |
| Shi Lü | 石绿 | #57C3C2 | 素绢 | 银红 | shilv-light / shilv-dark |
| Zha Ye Zong | 柞叶棕 | #692A1B | 熟宣 | 栗棕 | zhayezong-light / zhayezong-dark |
| Chang Chun Hua Lan | 长春花蓝 | #7EC0EE | 雪青 | 香叶红 | changchunhualan-light / changchunhualan-dark |
| Shan Geng Zi | 山梗紫 | #61649F | 雪青 | 满江红 | shangengzi-light / shangengzi-dark |
| Yan Lan | 鷃蓝 | #144A74 | 雪青 | 枣红 | yanlan-light / yanlan-dark |
| Fen Lü | 粉绿 | #83CBAC | 素绢 | 梅红 | fenlv-light / fenlv-dark |
| Yu Qin Lan | 玉鈫蓝 | #126E82 | 雪青 | 赭石 | yuqinlan-light / yuqinlan-dark |
| Pi Bian | 皮弁 | #8B5D33 | 赭纸 | 石青 | pibian-light / pibian-dark |
| Gan Lan Lü | 橄榄绿 | #5E5314 | 赭纸 | 满天星紫 | ganlanlv-light / ganlanlv-dark |
| Dai Zi | 黛紫 | #5D3A6F | 雪青 | 黛紫·深 | daizi-light / daizi-dark |
The roster is whatever the generator emits — it is not maintained by hand. Re-export this table from the node scripts/generate-themes.mjs stdout summary.
Configuration
Under the plugin's row in cordis.yml:
config:
defaultTheme: zhuqing-light
remember: true
hashSelector: true
settingsOrder: 40
License
MIT — see LICENSE.