Skip to content

Releases

v0.6.1

Choose a tag to compare

@shyuan-hub shyuan-hub released this 21 Oct 13:58
bfd1e78

dsh-compact-button v0.6.1

核心:两个纯显示层修复——dock 行的顺序撞车和过早出现。不改功能、不改通道、无破坏性变更。

PR:#7 | 已验证平台:DSH 0.2.1-rc.2(web)+ 0.2.0-rc.2(桌面版)
差异规模:9 files changed, +175 / −20


TL;DR

0.6.0 把两个按钮搬上官方 conversation.composer.dock 之后,实际用起来暴露出两处和平台自带药丸不一致的地方:

  1. 平台 0.2.1-rc.2 把原来的单个 stats 药丸拆成了 activity + usage 两个占用者,usage 正好占 order: 1——和插件的 order: 1 并列同名次,两个条目抢同一个显示位置。
  2. 官方药丸在会话产生模型活动之前是不渲染的,而插件从第一帧就渲染,于是空白会话上会冒出两个还没有可作用对象的按钮。

修法分别是 order: 1 → order: 10,以及给 ContextActionRow 加一道和官方同源的活动门控。


问题一:order 撞上了官方 usage

平台 0.2.0-rc.2 只往 dock 注册一个占用者,插件用 order: 1 刚好排在它后面:

占用者 order
stats 0

0.2.1-rc.2 把它拆成了两个:

占用者 order
activity 0
usage 1

于是插件和 usage 拿到同一个名次。list slot 按 order 升序排,同名次不保证稳定先后,结果是两个条目挤在同一显示位置而不是排成一行。

改成 order: 10:稳稳排在两个官方药丸之后,同时留出空位——后续官方再加占用者也不用回来改这个数。

-            // Sit after the official `stats` pill (order 0) that
-            // dsh-client-ui-chat registers into the same row.
-            order: 1,
+            // Sit after every pill the platform registers into this row.
+            // dsh-client-ui-chat ships `activity` (order 0) and `usage`
+            // (order 1) as of platform 0.2.1-rc.2 — a low number here would
+            // tie with an official pill, so 10 leaves headroom for more.
+            order: 10,

问题二:会话一开始就出现,而官方药丸要等首轮结束

官方那两个不是「晚一点注册」,而是组件自己 return null:

// ActivityPill
if (stats.steps === 0) return null

// UsagePill
if (usage === void 0 || billedInputTokens(usage) === 0 && usage.outputTokens === 0) return null

它们一直注册着,靠 slot 下发的 useProjection 判断会话有没有活动。插件这边无条件渲染,所以空白会话上就冒出来了——而那时候两个按钮都没有可作用的对象:没有历史可压缩,当前会话本身也就是新会话。

关键发现是 useProjection 本来就下发给了我们的组件。renderer 的 standardKit 会把 session 作用域的每个 hooks 源绑成 use<Name> 选择器、随 kit 一起交给槽位占用者——ActivityPill 的 inject 只给了 hooks.performanceUsage,却能解构出 useChat / useProjection,靠的就是这个机制。

所以不需要新增任何服务依赖,接上就行:

export function ContextActionRow({ compact, newSession, t, useProjection }: ContextActionRowProps) {
  // Both selectors are subscription hooks, so they run on every render and
  // must not sit behind the early return below.
  const stats = useProjection?.('sessionStats') as SessionStatsLike | undefined
  const usage = useProjection?.('tokenUsage') as TokenUsageLike | undefined
  if (!sessionHasActivity(stats, usage, useProjection !== undefined)) return null
  ...
}

⚠️ 两个 selector 都在早返回之前无条件调用。它们是订阅 hook,挂在条件后面会破坏 hook 调用顺序。


判据与三级降级

门控抽成纯函数 sessionHasActivity(stats, usage, haveSelector),便于单测覆盖每个分支:

优先级 判据 为什么
1 sessionStats.steps > 0 与 ActivityPill 同源。steps 数的是 step/end 事件,字面就是「首轮已闭合」;而且从完整 durable log 折叠,翻页和压缩都动不了它
2 tokenUsage.outputTokens > 0 dsh-session-stats 未装配、projection 缺席时的退路
3 都没有 → 显示 故意往这个方向降级:宁可按钮早出现,也不能判断失误让功能消失

第 1 条选 sessionStats 而不是 tokenUsage,是因为 steps 的语义就是「已闭合的 step 数」,和「首轮对话结束」字面对齐;tokenUsage 只在模型报出 token 后才有值,边界更松。


用户能感知到的变化

场景 0.6.0 0.6.1
新建空会话 输入框下方已有两个图标 无图标(与官方药丸同步出现)
首轮回复结束后 图标在 图标在(位置不变)
dock 行顺序 与官方 usage 抢位置 activity → usage → 本插件两个图标

升级须知

无破坏性变更,没有需要还原的旧状态,没有新增前置动作。

dsh plugin --profile web add dsh-compact-button@0.6.1
# 重启 dsh web(仅硬刷新浏览器不够),重启后再硬刷新页面

桌面版:插件由桌面 app 自己的插件管理界面装卸(dsh CLI 那句 profile "desktop" is managed exclusively by the Electron application 是平台设计)。更新后需要从托盘彻底退出再打开——桌面 app 有 background-close 行为,只关窗口不会重载 profile。


兼容性

  • 不声明任何 DSH peer 依赖,这条没变。client bundle 只 require react、react/jsx-runtime、@deepseek-ai/dsh-client-ui-primitives,三者都是平台 module table 里的 external
  • 平台 0.2.1-rc.2 下逐项核对过:conversation.composer.dock 仍是 kind: 'list' / scope: 'session';renderSlot("conversation.composer.dock", {}) 调用点未变;slots.inject / locale.register / sessions.binding / uiWorkspace.startSession 签名均未变
  • sessionStats 缺席时不会白屏。该 projection 由独立插件 @deepseek-ai/dsh-session-stats 提供,未装配时 useProjection('sessionStats') 返回 undefined,门控自动退到 tokenUsage;连 selector 都没拿到则直接显示
  • 降级行为温和。万一上游把 conversation.composer.dock 移除或改语义,slots.inject 等不到声明,结果只是「图标不渲染」——不抛错、不影响平台其余部分

已知限制

  • 依赖官方槽位 conversation.composer.dock:由平台声明与渲染,上游若移除则图标停止渲染(静默降级)
  • dock 行是居中 flex 容器,官方 dsh-client-ui-chat 已占用 order: 0(activity)与 order: 1(usage);本插件用 order: 10 排在它们之后,给后续官方条目留了空位
  • 图标现在要等会话有活动才出现。这是刻意的,与官方药丸一致;如果你希望它常驻,这是可讨论的取舍
  • 压缩按钮只负责提交 /compact,压缩的接纳与执行语义由 Host 侧 command-compact 插件拥有
  • 状态提示 4 秒后自动复位,不提供压缩进度展示
  • 新建会话沿用部署默认的 agent 预设与权限设置;若当前会话用的是非默认预设,新会话不会自动沿用

包信息与校验

dsh-compact-button-0.6.1.tgz    26,811 bytes
sha256  3152d9d31a86015f24f77bb81b76ad066d345390a918221c864741b6f8e6e6cb
sha1    0f0d776c802ff052e039ba8accb9740e3e63a2b5

包内清单(19 个文件,与 0.6.0 完全同构):

LICENSE  README.md  README.en.md  cordis.patch.yml  dsh.plugin.json  package.json
lib/index.js  lib/client.js  lib/client-registry.js
src/client/{CompactButton,ContextActionRow,NewSessionButton,index}.tsx
src/client/{compact-button.module.css,css-modules.d.ts,locales.ts,primitives.d.ts}
src/context-types.ts  src/index.ts
产物 通道
lib/index.js Host 半边(惰性 apply(),维持 bundle 挂载契约)
lib/client.js 官方 profile 通道(bundle id = 包名 dsh-compact-button)
lib/client-registry.js 插件注册表通道(bundle id = manifest id dsh-external/dsh-compact-button)

包内不含 patch-*.cjs——lib/index.js 里唯一一处 app.asar 是文档注释里那句「不再改它」,不是补丁代码。


验证结果

项 结果
pnpm typecheck ✅ 无错误
pnpm test ✅ 6 files / 59 tests passed(0.6.0 是 51,+8 全在门控)
pnpm build ✅ 三个 bundle 齐全
产物含 order: 10 ✅ 1 处
产物含 sessionStats / tokenUsage ✅ 各 3 处
产物含 conversation.composer.dock ✅ 3 处
产物含 dshCompactPulse keyframes ✅ 2 处
@deepseek-ai/dsh-client-ui-primitives 保持 external(未被 bundle 进来) ✅
装进 web + desktop 两个 profile,核对实际下发的 bundle ✅ 同上全部命中
桌面版 app.asar 无旧补丁残留 ✅ conversation.context.actions 命中 0 处(官方 conversation.composer.dock 在位)
lib/index.js 惰性 apply() 冒烟 ✅ typeof === 'function'、length === 0、返回 undefined、不抛错

新增的 8 条测试覆盖:门控三个分支(steps === 0 / steps > 0 / tokenUsage 退路)、无 selector 的显示降级、空会话不渲染、有 step 后渲染、以及 blank→active 的 rerender 转换。


升级 TL;DR(给赶时间的人)

  1. dsh plugin --profile web add dsh-compact-button@0.6.1
  2. 重启 dsh web,硬刷新页面
  3. 桌面版从托盘彻底退出再打开
  4. 空会话上输入框下方没有图标;发一轮消息后,activity → usage → 两个图标依次排开

v0.6.0

Choose a tag to compare

@shyuan-hub shyuan-hub released this 12 Oct 14:18
ed698aa

dsh-compact-button v0.6.0

核心:这两个按钮过去要靠给平台打补丁才能拿到落脚的槽位。从这一版起,它们搬到平台自己声明、自己渲染的官方槽位 conversation.composer.dock 上——本插件不再修改任何平台文件。

PR:#6 | 已验证平台:DSH 0.2.0-rc.2(web + 桌面版)
差异规模:27 files changed, +443 / −1179(净删 736 行,删掉的基本就是补丁机制本体)


TL;DR

以前每次 dsh web 启动,插件的 host 半边都要去就地改写已安装的平台 bundle(桌面版还要动 resources/app.asar),才能塞进一个官方没声明的 conversation.context.actions 槽位。现在 conversation.composer.dock 是平台公开的 list 型扩展点,官方 dsh-client-ui-chat 的 stats 药丸就注册在那一行——我们排到它后面就行了。

于是那套补丁机器整个删掉:不改 npm 里的平台 bundle,不改桌面版归档,桌面版自更新后零操作。

顺带把 UI 改成纯图标 + 平台原生 Tooltip,观感与旁边的上下文圆环对齐。

⚠️ 这一版有破坏性变更(删了公开入口、换了槽位名、按钮不再带文字),升级前请看下面的「破坏性变更」与「升级须知」两节。


新长什么样

按钮从「带文字的方块」变成两个图标,并排在上下文圆环左侧,共用输入框下方那条居中 dock 行。

状态 Tooltip(中 / EN) 图标表现
待命 压缩上下文 / Compact context 常态(三级标签色,与圆环同色系)
提交中 压缩中… / Compacting… 呼吸闪烁(1s 循环),按钮禁用防重复提交
已提交 已提交压缩 / Compaction submitted 业务蓝
未匹配 命令未匹配 / Command not matched 常态
失败 提交失败 / Submission failed 错误红

按钮没有文字后,状态改由两条通道共同承载:Tooltip 文本 + 图标颜色。任何时候悬停 200ms 都能看到「这个按钮是干什么的」以及「上一次点击的结果是什么」,4 秒后自动复位。

aria-label 与 Tooltip 气泡同源,读屏和鼠标悬停得到的是同一句话。


⚠️ 破坏性变更

1. 删除的入口点

文件 原来是什么
patch-run.cjs 手动打补丁的 CLI 入口
patch-context-meter.cjs 补丁模块本体(435 行,导出 patchSource / patchInstalledTargets / findTargetFiles / describeResult / resolveDshHome 等)
scripts/patch-desktop-asar.cjs 桌面版 app.asar 改写脚本
src/patch-module.d.ts 上述模块的类型声明

任何引用这些路径的脚本、CI 步骤、文档链接都会失效。 package.json 的 files[] 里对应条目也一并移除,装完包后这些文件不存在。

2. 槽位名变了

conversation.context.actions(我们自己注入的)→ conversation.composer.dock(平台官方的)。

旧槽位名在本插件里彻底消失,构建产物中 conversation.context.actions 命中数为 0。如果有别的东西依赖过那个名字,需要跟着改。

3. 按钮不再有可见文字

从「图标 + 文字」变成纯图标。依赖按钮文本做端到端测试的选择器(getByText('压缩上下文') 之类)需要改成 aria-label。

4. locales.ts 删了两个 key

tooltip 与 newSessionTooltip 在图标化之后没有任何组件读取,已删除。由于 package.json 的 exports 暴露了 ./src/*,dsh-compact-button/src/client/locales.ts 属于可达路径,所以这算一处对外可见的收窄:

// 0.5.x 可以,0.6.0 类型不通过
t('tooltip')

现在的活 key 集合被一条测试钉住,共 6 个:label / pending / submitted / rejected / failed / newSession。


桌面版:从此不需要碰 app.asar

桌面版和 web 版走完全相同的路径——插件装进 desktop profile 即可。

平台把 bundle 打进 resources/app.asar 这件事不再构成障碍:插件代码从 profile 读,槽位由归档内的平台代码自己渲染,双方都不需要被修改。

🔄 桌面版自更新后无需任何额外操作。更新只替换 app.asar,而 app.asar 本来就没被动过;profile 里的插件也不受更新影响。

(dsh CLI 仍不能管理 desktop profile——那句 profile "desktop" is managed exclusively by the Electron application 是平台设计,桌面版插件的装卸由桌面 app 自己的插件管理界面负责。这条没变。)


#4 那一整类问题,结构性消失了

#4 是「DSH_HOME 迁移导致补丁扫不到平台 bundle,插件静默失效」。0.5.1 靠镜像平台的 resolveDshHome 优先级把它修好了。

0.6.0 让这类问题不可能再发生——因为根本不再有 home 解析和文件扫描:

$ grep -rn "DSH_HOME\|resolveDshHome\|homedir" src test
(no matches)

不往平台树里写东西,也就不需要知道那棵树在哪。同理,TESTED_PLATFORMS 这个「已验证平台清单」也随补丁模块一起删了——兼容性不再由锚点是否命中决定,而是由官方槽位是否存在决定。


架构变化

client 半边(功能全在这里)

  • ctx.slots.inject('conversation.composer.dock', ...) → slots.register({ ..., order: 1 }),排在官方 stats(order: 0)之后
  • 单个 entry 内并排渲染 ContextActionRow(左 CompactButton、右 NewSessionButton),保证左右顺序确定,不受 list slot 自身排序影响
  • 两个图标都用平台 Tooltip primitive(side="top"、delayMs={200}),与圆环触发器行为一致
  • 提交通道没变:session.command('/compact') 与手敲 /compact 完全同路;uiWorkspace.startSession() 在同一 workspace 开新会话

host 半边(改为惰性)

apply() 是空实现——功能完全在 client 侧,Node 侧无事可做。

但导出必须保留:cordis.patch.yml 会把本模块挂到 Node 树并在 profile boot 时调用它,缺入口点会直接破坏挂载,而 no-op 不会。这条契约由测试守着(apply 存在、零参、返回 undefined、不抛错、不打日志)。

注意 cordis.patch.yml 仍然存在,但它是 cordis 的 bundle 挂载声明层——只负责「把本插件声明进 profile」,不涉及任何对平台代码的改写。名字里的 patch 容易误会,语义上跟旧的那套补丁机器无关。

类型与测试基建

  • src/context-types.ts:本地 slots.register 面补上 order 字段——真实 registry 的列表显示顺序走 order,不是 priority,这个区别是排到 stats 后面的关键
  • src/client/primitives.d.ts:用本地 ambient 声明描述 Tooltip,不引入 @deepseek-ai/* 依赖。原因有两条:该包在 registry 上的发布版本与实际安装版本不同步,加 devDependency 会引入一个装不出来的范围;更要命的是第二份 renderer = 第二份 slot registry,会直接破坏槽位注册
  • vitest.config.ts + test/stubs/dsh-client-ui-primitives.ts:把 primitives specifier alias 到本地 stub。Vite 在 transform 期解析 import,vi.mock 顶替不了一个根本没安装的包

升级须知

从 0.5.x 升级——旧补丁不会自动撤销

0.6.0 自己不补丁了,但它不会回滚 0.5.x 已经打进去的改动。如果你之前用过 0.5.x,平台文件里可能还留着旧的槽位注入。

Web(npm 副本):0.5.x 每次启动会就地改写 @deepseek-ai/dsh-client-ui-conversation/lib/client.js。想让平台文件恢复原样,覆盖重装即可:

pnpm install @deepseek-ai/dsh-client-ui-conversation --force
# 或重装对应版本的 dsh

桌面版:如果你跑过 scripts/patch-desktop-asar.cjs,那份 app.asar 里的注入还在。做一次桌面版自更新(或重装桌面 app)即可还原。

不处理会怎样:旧注入的 conversation.context.actions 渲染位现在没人注册了,会留一个空容器;不会抛错、不影响新图标工作,但视觉上可能有一条多余的空隙。建议还原。

升级步骤

dsh plugin --profile web add dsh-compact-button@0.6.0
# 重启 dsh web(仅硬刷新浏览器不够),重启后再硬刷新页面

从更早版本升级

0.4.x 及更早的用户,除了上面这条,还要对照 0.5.0 / 0.5.1 的 release note 看有没有遗漏的前置动作。


兼容性

  • 不声明任何 DSH peer 依赖。client bundle 只 require react、react/jsx-runtime、@deepseek-ai/dsh-client-ui-primitives——三者都是平台 module table 里的 external(见 tsdown.config.ts 的 CLIENT_EXTERNALS),服务全靠 cordis 注入。包管理器不会为了满足一个范围而拉出第二份 renderer
  • 降级行为温和。万一上游把 conversation.composer.dock 移除或改语义,slots.inject 等不到声明,结果只是「图标不渲染」——不抛错、不影响平台其余部分
  • Tooltip 签名漂移。本地 ambient 声明若与真实签名不符,多出来的 prop 只是被忽略;组件测试(stub 版)继续盯住 label / anchor 接线

已知限制

  • 依赖官方槽位 conversation.composer.dock:由平台声明与渲染,上游若移除则图标停止渲染(静默降级)
  • dock 行是居中 flex 容器,官方 stats 药丸占 order: 0,本插件用 order: 1。将来若出现更多占用者,视觉顺序可能需要再调
  • 压缩按钮只负责提交 /compact,压缩的接纳与执行语义由 Host 侧 command-compact 插件拥有
  • 状态提示 4 秒后自动复位,不提供压缩进度展示
  • 新建会话沿用部署默认的 agent 预设与权限设置;若当前会话用的是非默认预设,新会话不会自动沿用(client 侧 uiWorkspace.startSession 不暴露预设 / 权限选择)

包信息与校验

dsh-compact-button-0.6.0.tgz    24,765 bytes
sha256  695e981b1fc679d92de33522c4f5ea8bebc4363a062775f6e7f3513a59a574e0

包内清单(19 个文件):

LICENSE  README.md  README.en.md  cordis.patch.yml  dsh.plugin.json  package.json
lib/index.js  lib/client.js  lib/client-registry.js
src/client/{CompactButton,ContextActionRow,NewSessionButton,index}.tsx
src/client/{compact-button.module.css,css-modules.d.ts,locales.ts,primitives.d.ts}
src/context-types.ts  src/index.ts
产物 通道
lib/index.js Host 半边(惰性 apply(),维持 bundle 挂载契约)
lib/client.js 官方 profile 通道(bundle id = 包名 dsh-compact-button)
lib/client-registry.js 插件注册表通道(bundle id = manifest id dsh-external/dsh-compact-button)

包内不含 patch-*.cjs,files[] 里也无对应条目。


验证结果

项 结果
pnpm typecheck ✅ 无错误
pnpm test ✅ 6 files / 51 tests passed
pnpm build ✅ 三个 bundle 齐全
产物含 conversation.composer.dock ✅ 3 处
产物含 order: 1 ✅
产物含 dshCompactPulse keyframes ✅ 4 处
@deepseek-ai/dsh-client-ui-primitives 保持 external(未被 bundle 进来) ✅
产物含旧 conversation.context.actions ✅ 0 处
产物携带已删除的死 key 字符串 ✅ 0 处
lib/index.js 惰性 apply() 冒烟 ✅ typeof === 'function'、length === 0、返回 undefined、不抛错
平台保持原样加载 ✅ web 的 npm 副本与桌面版 app.asar 加载后均 marker=0(未被改写)

升级 TL;DR(给赶时间的人)

  1. dsh plugin --profile web add dsh-compact-button@0.6.0
  2. 重启 dsh web,硬刷新页面
  3. 从 0.5.x 升上来的话,覆盖重装一次 @deepseek-ai/dsh-client-ui-conversation 把旧补丁还原掉
  4. 输入框下方、圆环左侧应该出现两个图标;悬停 200ms 能看到说明

v0.5.1

Choose a tag to compare

@shyuan-hub shyuan-hub released this 29 Sep 16:59
b42f82c

dsh-compact-button v0.5.1

修复:#4 — bug:DSH_HOME 目录迁移造成的错误
已验证平台:0.1.2-rc.1、0.1.5-rc.1、0.1.5-rc.2、0.1.5-rc.3、0.1.7-alpha.2、0.1.7-rc.1、0.1.7-rc.2


TL;DR

把 DSH 的 harness home 搬到非默认位置(例如 DSH_HOME=D:\AppData\DSH)的用户,从这一版起按钮会正常渲染。此前补丁只认 os.homedir()/.dsh,迁移后的 profiles 树根本扫不到,插件装上了也是静默失效。

顺带把 DSH 0.1.7 纳入已验证范围(0.1.7-rc.1 / 0.1.7-rc.2),这是 #4 里一并提出的诉求。

纯补丁层修复,无功能变更、无破坏性变更:压缩按钮与新建会话按钮的行为、样式、文案与 0.5.0 完全一致。


修复了什么

1. $DSH_HOME 迁移后补丁找不到平台 bundle(#4 主因)

补丁脚本靠扫描 <home>/profiles 与 <home>/profiles/<name> 来定位 @deepseek-ai/dsh-client-ui-conversation(host 半边每次启动自愈走的就是这条路)。0.5.0 里这个 <home> 硬编码为 os.homedir()/.dsh,一旦 harness home 被 DSH_HOME 重定位,扫描就落空——不报错、不打补丁、按钮不渲染,用户端表现为"装了没反应"。

现在插件镜像平台自己的 resolveDshHome 语义来解析 home,补丁会跟着迁移走。

2. 显式配置的 harness home 同样跟随(#4 补充)

平台的 resolveDshHome(configured, env) 在 $DSH_HOME 之上还有一层显式配置(各 home-aware 服务收到的 config.dshHome)。0.5.1 把这一层也补齐,并给手动 CLI 加了 --dsh-home 入口。

3. 适配 DSH 0.1.7

0.1.7-rc.1、0.1.7-rc.2 已加入 TESTED_PLATFORMS。补丁锚点本身是格式无关的正则语义锚,没有为 0.1.7 改过任何锚点——只是把"验证过"这件事写实了。

4. 文档一致性

两个 README 的"已验证平台"列表上个版本停在 0.1.7-alpha.2,而代码里的 TESTED_PLATFORMS 已经走到 0.1.7-rc.2,现已对齐。


home 解析优先级

优先级 来源 示例
1 显式配置(对应平台 config.dshHome) --dsh-home D:\AppData\DSH
2 环境变量 DSH_HOME=D:\AppData\DSH
3 默认 ~/.dsh
  • 任一层都支持 ~ / ~/ / ~\ 展开,结果一律绝对化。
  • 空白值视为未设置:DSH_HOME=" " 不会把 home 挪走。
  • 只加不减:显式主目录是并集而非替换。平台的 profiles 树由 env 解析,而 config.dshHome 是按服务的覆盖,两者可能指向不同根;若让显式层直接取代 env,就会把 #4 刚修好的那条路重新弄断。所以探测顺序是 [configured, env/默认] 去重,补丁幂等,重叠零成本。

一处有意偏离平台

平台用 ??,空字符串的 configured 会赢并把 home 解析到 cwd。补丁脚本是要往树里写东西的,因为一个空串就开始探测当前目录并不安全,所以本插件让空 configured 继续穿透到 env / 默认。已在 JSDoc 中写明。


新增 CLI

# 手动提前打补丁,并指定一个非默认的 harness home
node node_modules/dsh-compact-button/patch-run.cjs --dsh-home D:\AppData\DSH
# 也支持 --dsh-home=D:\AppData\DSH

找不到目标时,告警现在会列出实际探测过的 home,并提示用 --dsh-home / $DSH_HOME 指路:

[dsh-compact-button patch] no @deepseek-ai/dsh-client-ui-conversation install found; ...
[dsh-compact-button patch] homes probed: C:\Users\me\.dsh — point at a relocated harness with --dsh-home <path> or $DSH_HOME

升级

dsh plugin --profile web add dsh-compact-button@0.5.1
# 重启 dsh web,然后硬刷新页面(仅刷新浏览器不够)

不需要手动重打补丁——host 半边每次 dsh web 启动都会自愈。本包依旧不含任何 install 脚本(不依赖 postinstall)。

从 0.5.0 升级无需改任何配置:没迁移过 home 的用户走的是同一条默认路径,行为不变。


验证记录

项目 结果
pnpm test ✅ 7 files / 81 passed(较 0.5.0 新增 10 条 home 解析与 CLI 用例)
pnpm typecheck ✅ exit 0
pnpm build ✅ lib/index.js、lib/client.js、lib/client-registry.js 全新构建
与平台实现对齐 ✅ 7 组 configured / env 组合逐一比对真实 @deepseek-ai/dsh-home-paths,全部一致
锚点未漂移 ✅ 对 npm 上 pristine 的 0.1.5-rc.3 / 0.1.7-rc.1 / 0.1.7-rc.2,5 个锚点各恰好命中 1 次
迁移 home 端到端 ✅ DSH_HOME=A + --dsh-home B → A、B 两处 bundle 均打上补丁;二次运行 already;取消 env 后 A 不再被发现(证明真由 env 驱动)
打包产物端到端 ✅ 解包 tarball 后重跑上述场景,6/6 通过(非源码目录验证)
包内容 ✅ 21 个文件,与 files 白名单一致,无 test / 多余产物泄漏
失败降级 ✅ 找不到目标时退出码仍为 0,不打断 dsh 启动

已知限制

  • 插件不解析 cordis 配置文件里的 config.dshHome。要做到这点就得引入平台依赖,与本包"零 DSH peer 依赖、不跟 DSH 发版走"的设计冲突。因此"显式配置"这一层通过 CLI --dsh-home 表达。若你的部署里 config.dshHome 与 $DSH_HOME 指向不同根,两处都会被探测并打补丁——宁可多打一处也不漏。
  • 0.2.0-rc.1 / 0.2.0-rc.2 尚未验证,未纳入 tested set。届时若锚点仍命中会正常打补丁并标注 (not in the tested set);若漂移则只是按钮不渲染 + 一条明确日志,不会污染平台文件。
  • 迁移后若仍看不到按钮,先看启动日志里的 homes probed: 一行确认探测范围,再核对 profile 下是否真的装了 @deepseek-ai/dsh-client-ui-conversation。

产物校验

dsh-compact-button-0.5.1.tgz   36,063 bytes
sha256  a56eb7420d758026a229a442cbf11de9e609f02928017fee442f65b6d55f6564

English

dsh-compact-button v0.5.1

Released: 2026-09-30 · PR: #5 · Fixes: #4
Verified platforms: 0.1.2-rc.1, 0.1.5-rc.1/2/3, 0.1.7-alpha.2, 0.1.7-rc.1, 0.1.7-rc.2

TL;DR

Users who relocated the DSH harness home (e.g. DSH_HOME=D:\AppData\DSH) now get their buttons rendered. The patch used to probe only os.homedir()/.dsh, so a relocated profiles tree was never discovered — the plugin installed silently and did nothing. DSH 0.1.7 is now in the verified set too.

Patch-layer only: no functional or breaking change. Compact and New Session buttons behave exactly as in 0.5.0.

What changed

  1. $DSH_HOME relocation (the #4 root cause) — the standard-root probe now resolves the harness home with the platform's own resolveDshHome semantics instead of a hardcoded ~/.dsh.
  2. Explicitly configured home is honored too — the layer above $DSH_HOME (the platform's config.dshHome), plus a --dsh-home <path> flag on the manual CLI.
  3. DSH 0.1.7 verified — 0.1.7-rc.1 / 0.1.7-rc.2 added to TESTED_PLATFORMS; no anchor was touched for 0.1.7.
  4. Docs consistency — both READMEs' verified-platform list caught up with TESTED_PLATFORMS.

Home precedence

# Source Example
1 Explicit config (platform config.dshHome) --dsh-home D:\AppData\DSH
2 Environment DSH_HOME=D:\AppData\DSH
3 Default ~/.dsh

~ / ~/ / ~\ are expanded, results are absolute, and a blank value counts as unset at every layer. The explicit home adds a probe rather than replacing one: the platform resolves its profiles tree from the env while config.dshHome is per-service, so narrowing to the explicit home would re-break #4. Probes run over [configured, env/default], de-duplicated; patching is idempotent.

One deliberate divergence: the platform uses ??, so an empty configured wins and resolves the home to the cwd. A patcher that writes into the tree should not start probing cwd because of an empty string, so a blank configured falls through to env/default here.

New CLI

node node_modules/dsh-compact-button/patch-run.cjs --dsh-home D:\AppData\DSH

When nothing is found, the warning now lists the homes it actually probed and hints at --dsh-home / $DSH_HOME.

Upgrade

dsh plugin --profile web add dsh-compact-button@0.5.1
# restart dsh web, then hard-refresh the page

No manual re-patch needed — the host half self-heals on every start, and the package still ships no install scripts. Users who never relocated their home follow the exact same default path as before.

Verified

81 tests pass (+10 new), typecheck clean, fresh build. Parity with the real @deepseek-ai/dsh-home-paths across 7 configured/env combinations. All five anchors match exactly once on pristine 0.1.5-rc.3 / 0.1.7-rc.1 / 0.1.7-rc.2 from npm. End-to-end: DSH_HOME=A + --dsh-home B patches both, second run is already, unsetting the env stops finding A. The packed tarball was re-tested after extraction (6/6), ships 21 files matching the files whitelist, and still exits 0 when nothing is found so dsh boot is never broken.

Known limitations

  • The plugin does not parse config.dshHome out of cordis config files — that would require a platform dependency, against this package's zero-platform-dependency design. The explicit layer is expressed via --dsh-home. If the two point at different roots, both get patched.
  • 0.2.0-rc.1 / 0.2.0-rc.2 are not yet verified and are outside the tested set.
  • Still no button after relocating? Check the homes probed: line in the startup log, then confirm the profile actually has @deepseek-ai/dsh-client-ui-conversation installed.

v0.5.0

Choose a tag to compare

@shyuan-hub shyuan-hub released this 23 Sep 13:16
152de62

v0.5.0 — 不再跟着 DSH 发版走

发布日期:2026-09-23
npm:dsh-compact-button@0.5.0
上一版:v0.4.1(6e0080c)

TL;DR

DSH 出新版本时,本插件不需要再改版本号或依赖范围了。补丁锚点改成格式无关的语义正则,兼容性由结构决定而不是由 semver 范围决定。已实测通过 0.1.2-rc.1 → 0.1.7-alpha.2 共 5 个平台版本。


为什么需要这个版本

之前 package.json 把四个 DSH 包 pin 在 ~0.1.2-rc.1。在 rc 阶段这意味着每发一个 rc 就得改一次本插件:

semver.satisfies('0.1.5-rc.1', '~0.1.2-rc.1')  // false
semver.satisfies('0.1.5-rc.1', '*')            // false  ← 连 * 都不匹配 prerelease
semver.satisfies('0.1.5',     '^0.1.2')        // true   ← 只有 stable 能跨

semver 只在同一个 [major.minor.patch] tuple 内承认 prerelease,所以 ~0.1.2-rc.1 天然拒绝 0.1.5-rc.1。

更糟的是这个 pin 还会主动制造故障:包管理器为了满足范围,会在 profile 里额外拉一份 dsh-client-ui-renderer——两份 renderer = 两个 slot registry = 槽位注册直接失效。而本插件运行期根本不 import 任何平台包。


本次变更

1. 移除全部 DSH peer 依赖(BREAKING-adjacent,但对使用者无感)

从 peerDependencies / devDependencies 中移除:

  • @deepseek-ai/dsh-api-session-controller
  • @deepseek-ai/dsh-client-locale
  • @deepseek-ai/dsh-client-ui-renderer
  • @deepseek-ai/dsh-client-ui-workspace

保留 react(真实 peer)与 optional 的 cordis。

为什么安全:client bundle 只 require("react"),host 半边只用 node:*,所有平台服务都通过 cordis 注入拿到,类型由 src/context-types.ts 的结构镜像描述。pnpm install 后依赖树少了 66 个包。

2. 补丁锚点改为格式无关的正则语义锚

patch-context-meter.cjs 的 5 条锚点不再是「精确 tab 缩进的源码字面量」,而是语义正则,容忍:

上游构建差异 是否仍命中
css module 重新哈希 ✅(前缀从 bar 条目现场捕获,从不写死)
tab ↔ 空格(整文件重排) ✅
var → const ✅
对象 key 去引号 ✅
jsx helper 改名 ✅
函数签名压缩成一行 ✅

安全模型完全保留:每条锚点仍要求「命中恰好 1 次」,否则整体不写入;已打补丁的文件靠 marker 跳过。真出问题时是「按钮不渲染 + 一条指名锚点的日志」,绝不污染平台文件、绝不阻塞 dsh 启动。

3. 启动日志自检

[dsh-compact-button] platform patch applied: .../dsh-client-ui-conversation/lib/client.js @0.1.5-rc.2

未测过的版本会额外标注 (not in the tested set)。TESTED_PLATFORMS 只是文档,从不卡补丁——出新版本时什么都不用改,启动一次看日志即可。

4. 新增英文文档

README.en.md,与中文 README 逐节对应(含版本兼容策略章节),两个文件顶部互链。已加入 npm files 随包发布。

5. 修正「新建会话」tooltip 的误导文案

tooltip 原文承诺 "the same agent preset and permissions",但 uiWorkspace.startSession() 不暴露预设/权限选择——当前会话若用非默认预设,新会话不会沿用。现改为「沿用部署默认设置」(zh/en 同步)。


兼容性验证

对真实 npm 安装的每个版本跑完整检查(非推测):

平台版本 补丁命中 node --check 槽位声明 服务 API 幂等
0.1.2-rc.1 ✅ patched ✅ ✅ ✅ ✅
0.1.5-rc.1 ✅ patched ✅ ✅ ✅ ✅
0.1.5-rc.2 ✅ patched ✅ ✅ ✅ ✅
0.1.5-rc.3(latest/next) ✅ patched ✅ ✅ ✅ ✅
0.1.7-alpha.2(alpha) ✅ patched ✅ ✅ ✅ ✅

服务 API 覆盖:slots.inject / slots.register / sessions.binding().session.command() / uiWorkspace.startSession() / locale.register()。

注意:dsh@0.1.5-rc.1 的 ^0.1.5-rc.1 实际会解析到 0.1.5-rc.2 的 client bundle(同 tuple 内更新的 rc,符合 semver)。所以 CLI 版本号 ≠ 实际 bundle 版本,这也正是「不跟版本走」的价值所在。


升级方式

dsh plugin --profile web add dsh-compact-button@0.5.0
# 重启 dsh web,然后硬刷新浏览器(Ctrl/Cmd+Shift+R)

⚠️ 若你之前用 file: 装过同名版本,pnpm 会认为依赖未变而跳过刷新,需先卸再装:

dsh plugin --profile web remove dsh-compact-button
dsh plugin --profile web add dsh-compact-button@0.5.0

已打过补丁的平台 bundle 会被 marker 识别并跳过(already patched),无需手动还原。


没有变的东西

  • 仍然不含任何安装脚本(不依赖 postinstall,任何包管理器安装都不会报错或被拦截)
  • 补丁仍然幂等 + 漂移即安全中止
  • 按钮行为、状态流转、i18n 全部不变
  • 仍然依赖 conversation.context.actions 这个由本插件注入的子槽位

已知限制(延续)

  • 补丁只作用于 @deepseek-ai/dsh-client-ui-conversation 的 lib/client.js;上游若拆分/改名该 bundle,findTargetFiles 会找不到目标,需要跟进
  • 压缩按钮只提交 /compact,接纳与执行语义由 Host 侧 command-compact 插件拥有
  • 状态提示 4 秒后自动复位,不提供压缩进度展示
  • 新建会话沿用部署默认 agent 预设与权限,不沿用当前会话的非默认预设

English

v0.5.0 — no longer tied to DSH releases

Every new DSH release used to force a bump here. The peer range is gone and the patch anchors are now format-agnostic semantic regexes: compatibility is decided by structure, not by a semver range. Verified against 5 platform versions from 0.1.2-rc.1 to 0.1.7-alpha.2.

Why the old pin was actively harmful: ~0.1.2-rc.1 rejects 0.1.5-rc.1 (semver admits a prerelease only inside its own [major.minor.patch] tuple — even * rejects prereleases), so every rc needed a change. Worse, satisfying that range made package managers pull a second copy of dsh-client-ui-renderer into the profile — two renderers means two slot registries, which breaks slot registration outright. The plugin imports nothing from the platform at runtime.

What changed

  1. Removed all four DSH peer/dev dependencies. The client bundle requires only react; the host half uses only node:*; every service arrives via cordis injection. 66 fewer packages in the dependency tree.
  2. Format-agnostic patch anchors. The 5 anchors tolerate indentation changes, quoting, var/let/const, the css-module hash (captured live from the bar entry, never hardcoded) and jsx helper renames. The exactly-once assertion and marker-based idempotence are fully preserved — a real structural change still aborts without writing, degrading to "buttons not rendered + one clear log line" and never polluting platform files or blocking dsh.
  3. Startup self-reporting. The log prints the detected platform version, with (not in the tested set) for versions not exercised. TESTED_PLATFORMS is documentation only and never gates the patch.
  4. English README (README.en.md), shipped in the npm package, cross-linked from both files.
  5. Fixed a misleading new-session tooltip. It promised "the same agent preset and permissions", but uiWorkspace.startSession exposes no preset or permission selection — a non-default preset is not inherited. Now says the deployment default.

Install / upgrade

dsh plugin --profile web add dsh-compact-button@0.5.0
# restart dsh web, then hard-refresh (Ctrl/Cmd+Shift+R)

If you previously installed the same version via file:, pnpm treats the dependency as unchanged and skips the refresh — remove first, then add. Bundles already patched are detected by marker and skipped (already patched).

Unchanged: still no install scripts, still idempotent and abort-on-drift, no change to button behaviour, state transitions or i18n.

v0.4.1

Choose a tag to compare

@shyuan-hub shyuan-hub released this 07 Sep 12:59
6e0080c

dsh-compact-button v0.4.1

适配 DSH 0.1.2-rc.1 — 平台补丁锚点更新 + 依赖版本 bump,功能代码零改动。

✨ 更新内容

  • 适配 DSH 0.1.2-rc.1:peer / dev 依赖全部从 0.1.2-alpha.2 更新到 0.1.2-rc.1
  • 平台补丁锚点更新(patch-context-meter.cjs):rc.1 给 conversation.composer.bar 新增了 4 个子槽位(input.overlay / input.overlay.left / input.overlay.right / composer.dock),第 5 条补丁的锚点从 conversation.input.model 条目改挂到新末项 conversation.composer.dock;其余 4 条锚点与 rc.1 真实 bundle 逐字节一致,未改动
  • 确认 rc.1 仍不原生声明 conversation.context.actions 槽位,自动应用的平台补丁继续必要
  • 客户端功能代码零改动:「压缩上下文」「新建会话」按钮、状态流转、中英文实时切换行为与 0.3.2 一致
  • README、测试注释中的版本引用同步更新

🔧 升级方式

# npm(发布后)
dsh plugin --profile web add dsh-compact-button@0.4.1
  • 本版本要求 DSH 0.1.2-rc.1 平台;仍在使用 0.1.1-rc2 平台的请继续用 0.3.2
  • 平台版本不匹配时补丁逐条断言失败、安全中止:按钮安静地不渲染,平台其余部分不受影响;host 半边在每次 dsh 启动时会自动重新打补丁(自愈)

✅ 验证

  • 补丁作用于 npm 上 0.1.2-rc.1 真实 ui-conversation bundle:5 条替换全部命中,打补丁后的 bundle 通过 ESM 语法检查(node --check)
  • 幂等性保持:对已打补丁的 bundle 重跑会跳过(marker 检测)

⚠️ 注意

  • 平台补丁仍是针对平台 bundle 的字符串级替换:若上游(rc.2 / stable)再次调整 composer.bar 槽位表结构,锚点需再次更新(README「已知限制」有说明)
  • 已知限制无变化,详见 README

v0.3.2

Choose a tag to compare

@shyuan-hub shyuan-hub released this 27 Aug 15:03

v0.3.2 — 自动平台补丁:任何机器上按钮都能直接出现

背景

此前有用户反馈 Windows 环境下插件按钮不可见。排查确认根因:官方 @deepseek-ai/dsh-client-ui-conversation@0.1.1-rc.2 并未声明 conversation.context.actions 槽位(GitHub 源码、npm 产物、全量槽位清单三重验证均为 0 匹配),而按钮依赖该槽位渲染。之前开发机可见,是因为本机打过一次手工补丁——这条路径无法分发给用户。

核心变化:补丁随包分发,全自动应用

本版本把平台补丁产品化并纳入插件包:

  • 🔩 启动自愈:每次 dsh web 启动时,host 半边自动定位已安装的平台 bundle(dsh-client-ui-conversation/lib/client.js),就地注入 conversation.context.actions 槽位的声明与渲染
  • 🔁 幂等且漂移安全:已打过补丁的文件按标记跳过;平台版本漂移(非 0.1.1-rc.2 布局)时逐条断言匹配失败即安全中止,不写入任何半补丁文件,按钮安静地不渲染,插件其余部分不受影响
  • 📦 零安装脚本:不依赖 postinstall——pnpm ≥ 10 默认屏蔽依赖构建脚本(ERR_PNPM_IGNORED_BUILDS),实测证明启动自愈已完整覆盖补丁链路,因此本包不含任何安装脚本,任何包管理器安装都干净无警告
  • 🛠️ 可选手动 CLI:如需在启动前提前打好补丁:node node_modules/dsh-compact-button/patch-run.cjs

其他改进

  • CSS 防御:所有 var(--dsw-alias-*) 增加 fallback 值,平台变量缺失时按钮依然可见
  • 构建依赖清理:移除硬编码的 @rolldown/binding-darwin-arm64(平台特定原生绑定按平台自动安装)
  • 测试:55 个单元测试通过,新增补丁幂等/漂移/定位/语法安全回归测试

版本说明

版本 状态
0.2.0 ⚠️ 缺少补丁注入,请勿使用
0.3.2 ✅ 当前版本,latest 已指向此版本

升级方式

dsh plugin --profile web add dsh-compact-button@latest
# 重启 dsh web,硬刷新浏览器(Cmd/Ctrl+Shift+R)

v0.2.0

Choose a tag to compare

@shyuan-hub shyuan-hub released this 24 Aug 14:16

v0.2.0 — 新增「新建会话」按钮
在上下文计量面板里,「压缩上下文」按钮右侧新增了一个「新建会话」按钮:一键在当前 workspace 开启新会话并跳转过去。
✨ 新特性
🆕 新建会话按钮:点击即调用 workspaces.startSession(),在当前会话所在的 同一 workspace 中开启一个新会话并自动跳转;新会话沿用部署默认的 agent 预设与权限设置
🖱️ 防误连点:点击后按钮短暂锁定约 1.5 秒,避免重复创建会话
🌏 中英文文案:新增 newSession / newSessionTooltip 词条,跟随 DSH 语言设置实时切换
🔧 变更
面板槽位条目从单个按钮改为并排容器 ContextActionRow:左「压缩上下文」、右「新建会话」,顺序由组件内部保证,不受 list 槽位条目排序影响
client 半边新增 workspaces 服务注入(inject 现为 ['slots', 'sessions', 'workspaces', 'locale'])
CSS 新增 .row 布局:两个按钮等宽并排,面板样式保持清爽
README 更新:功能说明、使用方式、已知限制同步,并加入动态演示
🧪 质量
新增 Vitest 单测体系:6 个测试文件、46 个用例,覆盖按钮相位机、新建会话锁定逻辑、组合布局与 client 接线
⚠️ 注意
安装/升级后需重启 dsh web 并硬刷新浏览器才能生效
已知限制:若当前会话使用非默认 agent 预设,新会话不会自动沿用(client 侧 session.create 不暴露预设/权限选择)
📦 安装
dsh plugin --profile web add dsh-compact-button@latest

v0.1.0

Choose a tag to compare

@shyuan-hub shyuan-hub released this 23 Aug 12:23

v0.1.0 — 首个正式版
✨ 新功能
🔘 一键压缩上下文:在 DSH Web 输入框旁的上下文计量面板(ContextMeter)中新增「压缩上下文」按钮,点击即向当前会话提交 /compact,把较早的对话历史压缩成摘要,为后续对话腾出上下文空间
🔁 与 /compact 完全同通道:按钮调用 session.command('/compact'),和手敲斜杠命令走的是同一条命令通道,压缩结果照常以命令行形式出现在对话流中,无任何特殊行为
📊 提交状态实时反馈:按钮文案随状态流转——待命 → 提交中 → 已提交 / 命令未匹配 / 提交失败,4 秒后自动回到待命
🌏 中英文实时切换:文案跟随 DSH 语言设置(zh / en),字典注册在 compactButton 命名空间
📦 一键安装:支持 dsh plugin --profile web add "file:<路径>/dsh-compact-button-0.1.0.tgz";包内自带 bundle patch,安装后自动写入 dsh.profile.bundles 并自动挂载,聚合包重复挂载时自动退让
🪶 零侵入:纯 client 半边插件(host 半为空 apply);槽位不存在时按钮安静地不渲染,不影响平台其余部分
🚀 安装
pnpm install && pnpm build && pnpm pack
dsh plugin --profile web add "file:<你的本地目录>/dsh-compact-button-0.1.0.tgz"
装完重启 dsh web 并硬刷新浏览器(Cmd/Ctrl+Shift+R)即可生效。
⚠️ 注意事项
依赖平台为上下文计量面板声明 conversation.context.actions 子槽位(需随附的平台补丁);槽位缺失时按钮不渲染,插件其余部分不受影响
本插件安装或更新后需重启 dsh web 才能生效,仅硬刷新浏览器不够
压缩的接纳与执行语义由 Host 侧 command-compact 插件拥有,本插件仅负责提交命令
🔧 环境要求
DSH 0.1.1-rc.1(@deepseek-ai/dsh-client-* ^0.1.1-rc.1)
Node.js ≥ 20、pnpm ≥ 10