dsh-composer-color-fix

September 3, 2026 · View on GitHub

English TL;DR — DeepSeek Harness (DSH) 0.1.1-rc.x styles its composer <textarea> as color: transparent; -webkit-text-fill-color: transparent and paints the text you actually read in a sibling .backdrop overlay. When that overlay falls out of sync with the textarea value (reliably reproducible after refreshing a long-lived session), you keep typing but see nothing. This is a client-only DSH profile bundle that detects the transparent-painted surface at runtime and forces the theme foreground colour back on. It is deliberately self-disabling: once upstream ships a composer with an opaque color, the shim does nothing at all, so you can leave it installed across upgrades.

修复 DSH(DeepSeek Harness)输入框「能打字但看不见字」的问题。

工作原理 / How it works

DSH Composer Color Fix 安装、探测与自动退役流程

主要处理路径、异常分支和人工检查点。 / The main processing path, exception branches, and human review points.


症状

在一个聊了较久的会话里:

  1. 输入框突然完全无法输入;
  2. Cmd+R 刷新后能输入了,但键入的字符不显示—— 光标在动、发送按钮会亮、底部状态栏字数在涨、@ 补全下拉也能弹出, 就是看不见字

新建一个会话却一切正常。所以它是会话状态相关的 bug,不是 DSH 整体坏了。

根因

@deepseek-ai/dsh 0.1.1-rc.2 里,composer(InputBar)是一个双层结构

/* packages/client/ui-conversation/src/client/skeleton/InputBar.module.css */
.input {
  position: absolute;
  color: transparent;                    /* ← 故意透明 */
  -webkit-text-fill-color: transparent;  /* ← 双保险,也透明 */
  caret-color: var(--dsw-alias-state-business-primary);
  background: 0 0; border: none; outline: none;
  overflow: hidden;
}
.backdrop {
  position: absolute;
  color: var(--dsw-alias-label-primary);  /* ← 你看到的字是这层画的 */
  pointer-events: none;
  overflow: hidden;
}
  • 真正持有 value 的是 <textarea>,但故意不可见
  • 你看到的字(含语法高亮、文件 chip、@ 引用)来自同位置的 .backdrop 叠加层;
  • 一旦 React 没把某次草稿更新同步进 backdrop,value 进去了、视觉上空了

这不是任何第三方插件造成的——@deepseek-ai/dsh-client-ui-conversation 的原生设计。

一个佐证:.input 的注释里写着「caret is the textarea's and every visible glyph is the backdrop's」,作者知道这个耦合,但没给失同步时兜底。

影响范围

版本状态
0.1.1-rc.2(npm latest,2026-08-21)❌ 有 bug —— 本仓库的目标版本
0.1.2-alpha.3(npm alpha,2026-08-31)✅ 已修:composer 重写为 Lexical contenteditablecolor: var(--dsw-alias-label-primary),透明层与 backdrop 全部移除
更新版本(推测)✅ 沿用重写后的实现

注意:官方仓库 deepseek-ai/deepseek-harness 的 issue tracker 是关闭的 (has_issues: false),所以这个问题全网没有现成的公开方案,也没有地方提交。

修复思路

本 shim 不猜类名、不改选择器,只做一件事:问浏览器「这块可编辑区域现在是不是被画成全透明了」,是就把主题前景色强行贴回去。

三个设计原则,都是为了让它在 DSH 升级后自动退役而不是崩掉:

  1. 运行时探测,不猜类名 textarea[class*="_input"] 这类写法依赖 CSS-module 哈希(uV2eYG_),换个构建就废。 这里只调 getComputedStyle()color / -webkit-text-fill-color 的 alpha 是否为 0。

  2. 内联 !important 终结特异性军备竞赛 按 CSS Cascade Level 4,作者样式的优先级是: 普通声明 < 内联样式 < !important 声明 < 内联 !important。 直接把属性写到元素的 style 上,无论未来 DSH 用什么选择器都盖不住它。

  3. 自废

    if (!isTransparent(fill) && !isTransparent(color)) return false;
    

    上游修好之后(alpha 提到 0.1.2+ 即生效),探针返回 false,shim 一行代码都不执行。 可以放心留着跨版本升级,不用惦记卸载。

覆盖范围:textarea / input / [contenteditable] 全扫,MutationObserver 盯整棵子树 (composer 挂载晚、切会话会重挂),外加 focusin + 三次延迟补偿。

安装

一键脚本(推荐)

git clone https://github.com/BaymaxStudio/dsh-composer-color-fix.git
cd dsh-composer-color-fix
chmod +x install.sh
./install.sh web          # 参数是 profile 名,默认 web

脚本会:拷贝源码到 ~/.dsh/profiles/<profile>/_shims/ → 软链到 node_modules/ → 写入 dependenciesdsh.profile.bundles → 用 dsh --dump-config 自检(失败自动回滚)。

装完必须重启 DSH web 服务——客户端 bundle 清单一只在服务启动时构建一次:

pkill -f "dsh web"
cd "<你的工作区目录>" && dsh web --no-open

然后在浏览器里 Cmd+Shift+R 硬刷新。

手动安装

SHIM=~/.dsh/profiles/web/_shims/dsh-composer-color-fix
mkdir -p "$SHIM/lib"
cp package.json cordis.patch.yml "$SHIM/"
cp lib/*.js "$SHIM/lib/"
ln -sfn "$SHIM" ~/.dsh/profiles/web/node_modules/dsh-composer-color-fix

再编辑 ~/.dsh/profiles/web/package.json,加两处:

{
  "dependencies": {
    "dsh-composer-color-fix": "file:/Users/<你>/.dsh/profiles/web/_shims/dsh-composer-color-fix"
  },
  "dsh": {
    "profile": {
      "bundles": [ "...", "dsh-composer-color-fix" ]   // 追加到末尾
    }
  }
}

三个必须知道的坑

  1. dsh.profile.bundlesdependencies ClientModuleRegistry.flush() 只扫 bundles。写进 dependencies 的包不会被装进 __DSH_BOOT__ 清单(DSH 没有 client 插件自动发现机制)。
  2. profile bundle 必须声明 dsh.bundle.patch,指向一个 cordis.patch.yml, 否则 loadProfile 直接抛错。
  3. HTML 里的 __DSH_BOOT__ 是服务启动时一次性构建的,注册新 bundle 后不重启不会生效。

验证

# 1) profile 配置能加载
dsh --profile web --dump-config

# 2) shim 出现在启动清单里
curl -s http://127.0.0.1:3080/ | grep -c "dsh-composer-color-fix"   # 期望 >= 1

# 3) bundle 能被服务
curl -s -o /dev/null -w "%{http_code}\n" \
  http://127.0.0.1:3080/plugins/dsh-composer-color-fix/client.js    # 期望 200

浏览器里 DevTools Console 跑一行看补丁有没有打上:

document.querySelector('textarea').dataset.dshTextFix   // 期望 "1"

打上之后 getComputedStyle(textarea).color 应该是实际颜色(如 rgb(15, 17, 21)), 而不是 rgba(0, 0, 0, 0)

卸载

./install.sh --uninstall web

或手动三步:删软链 → 从 package.jsondependenciesdsh.profile.bundles 里去掉条目 → rm -rf ~/.dsh/profiles/web/_shims/dsh-composer-color-fix,然后重启 DSH。

已知副作用

启用后,textarea 本身的文字会显示出来,而 backdrop 仍在原位置画它的富文本层。 因此在粘贴图片、@ 引用文件产生 chip 的位置,两层会有轻微叠影。 不影响输入与发送——这是「看不见字」和「chip 偶尔叠一下」之间的取舍。

给上游的话

根因不在插件层,而在 InputBar 的双层耦合本身:把「持有 value」和「负责显示」拆到两个元素上, 就必然要承担它们失同步的风险,且失同步时没有降级路径(用户拿到的是一个静默不可用的输入框)。

0.1.2-alpha.3 改成 Lexical contenteditable + 正常 color 是对的方向。 在此之前,更稳妥的兜底是:backdrop 未跟上时,让 textarea 自己显示文字。

详细的 bug report 模板见 docs/UPSTREAM-REPORT.md

环境

在以下环境实测通过:

  • macOS (darwin),Chrome
  • @deepseek-ai/dsh 0.1.1-rc.2,profile web
  • 同时加载 8 个 profile bundle(含 @linxin666/dsh-web-ui-all@nanmicoder/dsh-agent-teams@omdsh-dev/dsh-genui@anionex/dsh-vision-toolkit

License

MIT