Repository navigation
Releases: dingminhua/dsh-connect-qoder
Release list
v1.14.0 — 卡片新增只读「活动」区,上游下发的活动整份读出来
📣 v1.14.0 — 卡片新增只读「活动」区,上游下发的活动整份读出来
Qoder 的 sash/api/v1/me/campaigns 此前在本插件里只被当作签到接口用:签到路径
只挑 actionType === 'CLAIM_BENEFIT' 的那一条,而 VIEW_DETAILS 这类纯展示活动
(「1024 创造力集结」「首月订阅 Credits 翻倍」这些 banner)在解析层就被整行丢掉了。
用户实拍截图里这些活动一个都不出现——本版把活动面整份读出来,并在卡片上加了
一条只读的「活动」列表。
一、签到路径与展示路径共用一份解析
新增 fetchCampaignFeed()(src/upstream.ts)返回整份活动面(活动列表 + 顶层
campaignUrl / showCampaign / claimable),纯函数 projectCampaignEntry() 把
placements[*].content.{zh,en} 收拢成标题 / 描述 / detailUrl / buttonText;
原有的 fetchCampaigns() 退化成它的窄投影。
这样做的理由是避免两条路径各写一份解析、日后漂移:签到找可领那条、卡片渲染整份,
读的是同一个 projectCampaignEntry。签到行为本身没有变化(旧实现同样返回全部行,
过滤只发生在 claimDailyCredits 内部)。
二、文案原样来自上游,不发明标题也不伪造链接
活动标题与描述逐字来自上游(按界面语言取 zh / en,缺哪边用另一边);
「查看详情」是真链接,指向上游给的 detailUrl,缺失时退回活动落地页。解析对字段
缺席宽容:活动形状按节日变,缺字段就少渲染一点,绝不拿 campaignId 编一句
「限时活动」。
读不到 ≠ 没有:上游没下发时显示「当前没有活动。」,读取失败时显示原因,而不是
把失败伪装成空列表。
三、新增只读路由
GET /plugins/dsh-connect-qoder/campaigns(src/status-paths.ts +
src/web-status.ts),与 /account-credits 同款形状:非 GET 405、未接线 503、
读失败 502。只读:不签到、不写账号。
四、验证
- 新增测试:
tests/upstream.spec.ts(feed 解析 / 窄投影 / 幂等)、
tests/campaigns.spec.tsx(客户端渲染 10 例)、tests/web-status.spec.ts
(路由 200/503/502/405)。 - 维护者审查后并入两处修正:探测脚本
probe-campaigns.mjs原写死了 tsdown 带
hash 的分块名(必然ERR_MODULE_NOT_FOUND),改为直接导入源码;同时补回插入
活动样式时被误删的.dsm-qoder-composer-panel-error规则。 - 全量 982 条测试通过,typecheck 零错;CI 的 ubuntu 与 windows 两个 job 转绿后才发布。
五、已知边界
活动列表只在卡片挂载与切换区域时拉取,不参与 60s 轮询——活动是只读内容,够用
即可。活动区当前放在卡片正文、区域作用域内(额度读数之下的同层);组件本身不绑定
容器位置,位置若需调整只需改挂载那一行。
v1.13.0 — 上下文档位的 400K 一律按 500K 呈现与保存
🎚️ v1.13.0 — 上下文档位的 400K 一律按 500K 呈现与保存
Qoder 设置卡片里的上下文预算下拉,中间那一档此前显示 400K(上游 context_config
至今声明 [200000, 400000, 1000000],两个区域、实时刷新均核实)。本版起这一档在
插件内一律按 500K 呈现与保存:下拉显示 200K / 500K / 1M,升级前勾选过 400K
的存档读回即 500K,不会留下一个命中不了任何选项的选中值。
一、改写在解析边界,而不是渲染层
档位表在本插件里只有一个写点(parseUpstreamModel)与一个存档读点
(modelFromStored)。新增的改写表 QODER_CONTEXT_TIER_OVERRIDES 就落在这两处
之间:卡片下拉、usage 投影、落盘往返、运行期预算四个读点拿到的是同一份改写后
的表。在渲染层改只会让它们重新变成两张表——「下拉里的选项」与「保存后生效的窗口」
对不上,正是这个文件历史上踩过的坑。上游哪天真的改口发 500K,这张表自动变成恒等
映射,不需要再动任何读点。
默认档(上游把 is_default 标在 400K 上时)同步改写:只改表不改默认档,会出现
「选中值不在选项里」的下拉。
二、不替上游说话
档位是插件本地的上下文预算:聊天请求体里没有任何上下文字段(实测
createChatBodyTemplate),网关按模型自己的配置服务请求。所以这次改写只影响 DSH
侧的预算选择与显示,不构成对上游窗口的任何承诺——这一点写进了 CHANGELOG 与代码
注释,避免后来人把它读成「上游支持 500K」。
三、验证
- 新增测试 6 条:改写表单值/整表去重升序/保存预算迁移/默认档改写/表外值不受影响
(tests/catalog.spec.ts),以及一条端到端(tests/index.spec.ts)——存档里留
改写前的预算 400K 与旧快照,读一次/usage,文档里的档位表、生效窗口、回显预算
三处必须都是 500K。 - 全量 958 条测试通过,typecheck 零错;CI 的 ubuntu 与 windows 两个 job 转绿后才发布。
v1.11.0 — 上游报额度耗尽必显示:池关着时那次失败也有出口
🚨 v1.11.0 — 上游报额度耗尽必显示:池关着时那次失败也有出口
账号池的逐账号明细只在池视图里,而池的记账有两道门——池关着不记盘、非成员不记。于是池关着时,对话路径上一次上游报「当日额度用尽」(业务码 110)的失败,在任何 UI 上都无处可看:用户只看到请求失败,卡片上的余额还是旧数。
0.7.x 时代有个「今日额度疑似已用尽(本地统计,仅供参考)」胶囊专门补这个缺口,它在后来随本地请求计数器一起被拆掉了,缺口就一直留着。本版补上它——但用的是上游明说的事实,而不是本地推断。
上一版 1.10.0 发的是跨连接器余额对比(issue #14),与本版互不掺杂:1.10.0 的产物里不含本版的任何代码。
一、上游报额度耗尽必显示
- 对话路径上一旦上游业务码命中配额族(
QUOTA_CODES:110 当日用量 / 113 配额 / 115 免费 / 116-118 团队与个人 Credits / 119 免费额度 / 122 Billing Group / 402 / 6004),该区域的卡片出现一条通知:最近一次时刻 + 业务码,悬停是脱敏后的上游原文。 - 与账号池开关无关:池开着且命中号是成员时,逐账号明细(红点 + 原因)在池表格里;这条区域级通知补的是「最近一次」的时序上下文。池关着时,它就是那次失败唯一可见的出口。
- 措辞是「上游报」,不是旧版的「疑似(本地统计)」:这份记录只由上游明说的业务码产生,不是本地推断——上游明说过,就别再说疑似。
- 限流不会被标成耗尽:
rate-limited(HTTP 429 短窗)映射的是池的短冷却,把它标成耗尽会把一个还有额度的号误判成「今天没救了」;这条界线有测试钉住。 - 进程内状态、不落盘:它说的是「上游最近一次说过」,不是「现在仍然耗尽」——上游恢复不会主动通知,时效由时间戳表达,重启清零可接受。
- 上游原文走既有脱敏路径:与账号池探针同一条
redactUpstreamText,PAT(pt-形态)绝不进通知原文,有测试钉住。
二、验证
- 本机
pnpm run check全绿:typecheck + 测试 970 passed / 8 skipped + build。 - GitHub CI 的
ubuntu-latest与windows-latest两个 job 均 success。 - 新增用例覆盖判据三条(只有
quota-exhausted才记、限流不算耗尽、PAT 不进原文)与卡片渲染一条(带字段即渲染、悬停挂脱敏原文、不带字段一条都不画)。
本版只新增一个 usage 文档的可选字段(
lastQuotaExhaustion,缺席 = 上游从没说过);请求形状、落盘格式、配置键、CLI 与宿主 peer 范围一律未动。宿主 0.2.1-rc.3 上无需任何适配。升级后请重启对应 profile 的 DSH 进程。
v1.10.0 — 跨连接器余额对比:别的连接器当家时也显示积分
💳 v1.10.0 — 跨连接器余额对比:别的连接器当家时,也看得见本家的余额
同时装多个连接器(比如 Qoder 与 WorkBuddy)时,积分胶囊本来只跟着会话当前选中的 provider 走:两家的 provider 映射表互不相交,选了谁家的模型,另一家的读数立刻从输入区消失——而卡片里的「显示位置」设置救不了它(provider 的判断在前,位置判断在后)。于是想比较两边余额再决定切哪家,只能来回切模型看余额。
本版给每个区域加一个 opt-in 开关:勾上之后,别家模型当家时,本区域的读数仍然显示在原位,与对方的读数并排常驻,两边余额一眼可比。
开关默认关闭,默认行为逐字不变——多渲染一个胶囊是所有用户都会注意到的变化,必须是你自己要。
一、跨连接器余额对比(issue #14)
- 卡片积分区新增第三行「其他连接器的模型选中时也显示」(
regions.*.showCreditsForForeignModels,默认false),与「在对话框显示积分」「显示位置」并列,同样整行可点、立即生效,不需要点保存。 - 勾选后的行为:会话模型属于别的连接器(或模型投影尚未落地)时,第一个开了开关、且本区域的显示/启用开关都放行的区域照常渲染;两个区域都开时取国内版(区域顺序,可预测)。
- 位置选择照旧把关:读数仍可放在「输入框工具栏」或「输入框下方」,对比态不影响这个选择。
- 区域开关照旧把关:显示与启用开关、席位选择在对比态下一律照常生效。
- 悬停文案分流:对比态下写的是「Qoder 侧计费账号 {name} 的余额」——此时本会话并不经 Qoder 计费,沿用「当前计费」会让人误以为这个号在给本会话付钱。
二、顺带修掉的一处守卫错误
胶囊的挂载取数 effect 原本挂在「provider 是否属于本插件」上,而请求真正用的是区域。对比模式下 provider 是外来的,effect 整个不跑——胶囊画出来了,余额却永远显示「—」。守卫改为按区域判定(activeRegion),并由新增测试钉住。
三、验证
- 本机
pnpm run check全绿:typecheck + 测试 962 passed / 12 skipped + build。 - GitHub CI 的
ubuntu-latest与windows-latest两个 job 均 success。 - 新增
tests/composer-foreign-credits.spec.tsx共 18 条用例,覆盖:默认关时的兼容面、区域选择与顺序、显示/启用/席位三道原有关卡照旧生效、模型投影未落地、对比态与原生态的悬停文案分流、写入不伤到隔壁区域。
本版不含「上游报额度耗尽」那条通知(那是下一版 1.11.0 的内容),请求形状、落盘格式、CLI 与宿主 peer 范围也都没有变动。
v1.9.2 — 宿主基线推进至 0.2.1-rc.3,本插件无需运行时代码改动
🛠️ v1.9.2 — 宿主基线推进至 DSH 0.2.1-rc.3,本插件无需运行时代码改动
本版是一次宿主适配版:DSH 桌面端升到 0.2.1-rc.3,我们逐包比对了 rc.2 → rc.3 的全部变更,确认本插件用到的宿主包与槽位契约一字未改,因此运行时代码保持原样,版本号按 patch 递增。
与以往不同,这次宿主换代还带来一个环境层面的变化:宿主 app 目录名从 DSH NEXT.app 变为 DeepSeek Harness.app,指向旧路径的 dsh 命令行 shim 会直接失效——README 里那条 dsh plugin --profile desktop add @dmh2002/dsh-connect-qoder 因此也跑不通。本版把修法写进了中英双语 README。
一、rc.3 到底变了什么(逐包比对结论)
- 插件依赖的宿主包(
dsh-llm/dsh-llm-pi-ai/dsh-settings/dsh-atomic-write/dsh-attachment/dsh-host-webserver/dsh-home-paths/dsh-client-ui-slots/dsh-client-ui-settings/dsh-client-ui-settings-plugins/dsh-client-ui-renderer/dsh-client-locale)与 rc.2 逐字节相同,只有package.json的版本号不同。 - rc.3 的客户端改动全部是增量的,且都落在本插件用不到的地方:会话头的 View 标签页置顶(新增
conversation.session.header.views.actions槽位)、草稿接管新增whenDraftExists(replace/append/keep)、primitives 的Tooltip支持left侧并按视口自动翻转、Menu新增current与minWidthFromAnchor。 - 宿主内置 pi-ai 仍是
1.1.0,上下文归一化契约不变——所以 1.1.0 修掉的那个「静默丢工具」缺陷不会复现。 - peer 范围
>=0.2.1-rc.1 <0.3.0-0在 npm 标准语义与宿主兼容门禁语义下都放行0.2.1-rc.3,声明一个字没改。
二、本次实际做了什么
- 回归覆盖:
tests/dsh-line.spec.ts的ADMITTED_PRERELEASES补进0.2.1-rc.2/0.2.1-rc.3,host gate 用例补进0.2.1-rc.3,把「当前宿主版本确实落在声明范围内」钉住,防止宿主再推进 rc 时静默失配。 - 文档登记:README 中英双语新增「当前宿主基线 0.2.1-rc.3」小节,逐项列出上面的比对结论与实测结果(宿主内加载状态、槽位占据情况、兼容告警情况、回归结果)。
- 宿主换代的环境修复:
dsh命令行 shim 改指新宿主自带的 CLI 入口DeepSeek Harness.app/Contents/Resources/runtime/cli/bin/dsh(内部以ELECTRON_RUN_AS_NODE运行app.asar里的dsh/lib/bin.js),修法同样写进 README 的安装一节。
三、验证
- 本机
pnpm run check(typecheck + test + build)全绿;测试 948 passed / 8 skipped。 - GitHub CI 的
ubuntu-latest与windows-latest两个 job 均 success。 - 宿主
0.2.1-rc.3实机加载:配置目录里include:dsh-connect-qoder状态schema;模型选择器槽位conversation.input.model.label由dsh-connect-qoder-client(priority-1)占据;composer 槽位conversation.input.left有dsh-connect-qoder-composer-credits;dsh plugin list对本插件无兼容性告警。
升级后请重启该 profile 的 DSH 进程,让新版本生效。
v1.9.1 — 积分胶囊在池接管时显示计费号的余额
🔍 v1.9.1 — 积分胶囊在池接管时显示计费号的余额
修一处「显示的是错的号的余额」。用户实拍:面板高亮「当前选用 丁晓潇 100」,胶囊却读「QD AI · 773」——773 是已用量上限、根本没在计费的 ddd。
根因:两个数来自两条判据
- 胶囊读 usage 文档的文档级
credits。那份文档是refreshUsage用第一发目标(区域选中号)读上来的,所以它天然是选中号的余额。 - **面板「当前选用」**按请求路径真判据(
src/shim.ts:第一发被硬拒才由rankPool第一名接管)落在接管者身上。
池开且选中号被排除时,两者必然分叉。这不是显示层的小错——胶囊给了一个误导性的「我还有 773」,而真实在烧的是 100 的那个号。
修法
胶囊改读此刻在计费那个号的余额,三级回落:
- 表格逐号读数(面板打开 / 切号 / 一键测试后取,最新);
- 同一份 usage 文档里池视图的每行余额(composer 拉的是 full 文档,恒带,零额外请求);
- 仅当计费号就是文档自己的号时才退回文档级总额。
拿不到计费号的余额就显示「—」:说不知道,比说一个错的号强。
另两处连带修正:
- 悬停写明这个数字是谁的(
composer.billingAccount,双语)。接管发生时「QD AI · 100」本身不说归属,正是截图困惑的来源。 - 面板选中行缺读数时的回填改用文档总额(它本来就是选中号自己的余额),不再用胶囊显示值——否则接管时
ddd行会被写成接管号的 100。
兼容性
旧宿主 / 池关(无 current 行)时退回选中号 = 文档总额,行为与 1.9.0 逐字相同;无接管时两路同值。版本号取 patch:只改「显示谁的余额」,不改判据、请求形状、落盘格式、配置键、CLI 与 peer 范围。
测试
新增 tests/composer-billing.spec.tsx 7 条:截图现场(表格路径与池视图路径各一条)、无读数退「—」、旧宿主兼容、无接管同值、面板打开后的表格路径、行回填不串号。变异 4/4 被拒:胶囊退回文档总额 ✗、current 判据改 selected ✗、行回填用显示值 ✗、悬停不说归属 ✗。
安装
npm install @dmh2002/dsh-connect-qoder@1.9.1v1.9.0 — 活动徽标走进模型选择器
🔍 v1.9.0 — 活动徽标走进模型选择器
这一版让「错峰 4 折」「免费」两枚徽标出现在做选择的地方——宿主的模型选择器。issue #12 的观察一针见血:徽标原先只画在设置卡片里,而卡片是设置面,等你翻到那里读到「今晚打折」时,模型早选完了。同一个上游事实,现在画在还能改变决定的位置。
一、选择器里画什么
- 活动徽标三态与卡片完全一致:窗口内实心、窗口外灰显虚线(悬停写明几点生效)、没有生效时间可说就整枚不画。判定复用
promoDisplayAt()(1.8.1 引入),文案复用promoBadgeLabel/Tooltip——上游原文,插件不翻译不改写。 - 免费标签:
is_free(倍率 0)的模型挂「免费」胶囊,悬停「倍率为 0,不消耗积分」。 - 样式零新增:
.dsm-qoder-model-promo*/.dsm-qoder-model-meta-tag一族直接复用,只加一个菜单行限宽修饰类(trigger 行宽、菜单行窄,不限宽时长文案会把模型名挤成省略号——主次颠倒)。两个表面不各写一套,观感永不漂移。
二、数据从哪来
- 读卡片自己的路由(
/plugins/dsh-connect-qoder/usage)但带detail=fast:只答本地已有状态,不触发上游重读——选择器每开一次都去烧一次上游请求是不成比例的。 - 快照按区域共享缓存(60s TTL)+ 单飞:菜单里几十行同区只发一次请求。
- 窗口边界的翻面靠共享分钟时器重读时钟,不依赖新数据到达:22:00 开启、08:00 结束,挂着的选择器最迟各差一分钟翻面。所有挂载实例共用一个 timer,最后一个退订时关掉。
三、single 槽的抢位与交还(跨插件共存)
conversation.input.model.label 是 single 槽:只渲染第一条注册,同优先级重复注册直接抛。workbuddy 占着默认优先级 0,且它给所有模型画东西(倍率后缀、忙闲图标、「新」标记)。
- 本插件以
priority: -1抢占——抢位后外来 provider 必须交还:用原始entries()找到账本里下一个组件原样委托(entriesOfSlot()对 single 槽去重后只剩自己,拿不到别人)。不交还的后果是「qoder 一到场,workbuddy 的标签集体消失」。 - 委托渲染成子元素而非函数调用:被委托组件自带 hooks,直接调用会把它的 hooks 混进本组件的 hook 序列——同一槽位实例在外来↔本插件 provider 之间切换时,hook 数量一变 React 直接崩(有测试钉住)。
- 组件由工厂按注册闭包捕获
slots:bundle 是单例,模块级变量会在多 fiber 各跑一次apply时被后者覆盖——委托会读到错误宿主的账本(issue #12 报告的本地补丁正是这个写法,已修)。 - 两个都委托的插件理论上存在互委托环;宿主的错误边界会把抛错的 entry 一次性弃权(下一帧换幸存者),不会死循环。当前生态(workbuddy、1017 参考实现)都是「渲染裸名」的终点型组件,不成环。
- 旧宿主线(0.2.1-rc.2 之前)没有这个槽:
inject永不兑现或注册抛错被 catch 成一行日志,卡片与 provider 不受影响,选择器退回宿主裸名。
四、这一版的测试
新增 tests/model-label.spec.tsx 18 条:三态渲染、免费标签、菜单/trigger 限宽、key/显示名两列匹配、fetch 失败退裸名、委托转发 ownerProps(且绝不把本插件的 t 递出去——那是绑在 settings.qoder 上的,递出去等于让别人拿我们的字典读它自己的 key)、同实例切换 provider 不崩、两 fiber 同时存活互不覆盖、单飞、纯时钟翻面。
变异验证 8 个变异体全部被拒:priority 改默认 ✗、委托改函数调用 ✗、不过滤自己 ✗(worker 崩溃)、转发 t ✗、绕过 promoDisplayAt ✗、工厂改模块级变量 ✗、时器不重渲染 ✗、单飞失效 ✗。settings-rebind 的槽位计数 4→5,并新增「注册必须带 priority: -1 + locale」断言。
五、安装
npm install @dmh2002/dsh-connect-qoder@1.9.0peer 范围不变(宿主 ≥0.2.1-rc.1 各线均装得上;选择器徽标只在声明了该槽位的 0.2.1-rc.2+ 线上出现,更早的线静默退回裸名)。
issue #12 由 jinlei81 报告并附本地验证补丁,五个坑(priority 冲突、委托必要性、entries() vs entriesOfSlot()、无模块级 t、组件拿不到 ctx)全部核实成立;其中「无模块级 t」由注册时的 locale: namespace 解决(渲染器按 entry 注入命名空间绑定的 t,与宿主 1017 参考实现同机制),「ctx 捕获」改成了工厂闭包(见上)。
v1.8.1 — 免费徽标补悬停、错峰窗口外灰显
🔍 v1.8.1 — 免费徽标补悬停、错峰窗口外灰显
这一版补齐两枚模型徽标「看不出来」的那一半:「免费」有了悬停说明(为什么免费),「错峰折扣」在窗口外不再整枚消失(白天也看得出晚上有折扣)。都是从实机截图反馈来的:14:5x 打开卡片,Qwen3.8-Max 行上既没有「错峰 4 折」、悬停也问不出什么——而它的规则、文案、窗口在目录里全都是齐的。
一、「错峰 4 折」在窗口外改成灰显
旧实现只在「此刻成立」时画,于是白天打开 DSH 的人永远发现不了晚上有折扣。新增 promoDisplayAt() 三态:live 实心、pending 灰显、none 不画。
- 灰显挂的仍是上游那句原文案(「错峰 4 折」),但换成虚线描边 + 中性底色 + tertiary 灰字——虚线是形状差异,不依赖颜色,色觉障碍与高对比度主题下仍成立。只调深浅会被读成「现在就在打折」。
- 悬停里前置一句状态:「当前未生效,22:00 起生效」,再接上游的时段与时区。信息一条不少,只多一句「还没到点」。
- 两种情形仍整枚不画:窗口读不懂(给不出「几点生效」——猜一个时间比不说更糟)、没有窗口而
active为 false(常驻活动此刻没开,同样没有生效时间可说,画出来只能是一句空话)。
判定函数 promoLiveAt() 签名与语义不变(现在实现为「promoDisplayAt 等于 live」),两个函数共用同一套前提。
二、「免费」补上悬停
它不是活动徽标,是上游的 is_free(倍率 0)——上游对它没有 description / 时段 / 链接可引用,所以这句解释由插件自己写:「倍率为 0,不消耗积分」。
加它的理由:卡片上「免费」与「错峰 4 折」并排,后者才是限时的那个。两个字的徽标不说清判据,就会被读成「这模型在搞活动」。
三、这一版的测试
新增 9 条(promo.spec.ts 6 条三态判定、card-promo.spec.tsx 2 条挂载断言、locales.spec.ts 2 条对着真表钉文案)。其中文案测试断言的是 key(row.modelFreeHint / row.promoPending)而不是某句中文——key-preserving 的假翻译器只能钉住「用了哪个 key」,说不出这句话会不会误导人;措辞另由真表断言(「免费」的悬停必须写出判据且不能出现「限时」,灰显前缀必须真的把 {start} 插进去且点明「未生效」)。
三个变异逐个验证过测试真抓得住:窗口外返回 live(退回旧行为)⇒ 5 条红;去掉 windowUnreadable 守卫 ⇒ 1 条红;免费徽标去掉 title ⇒ 1 条红。
完整逐条记录见 CHANGELOG.md。升级方式照旧:更新插件后重启一次 DSH,新卡片即生效。
v1.8.0 — 设置卡片重排:总开关回到它控制的一切之前
🧭 v1.8.0 — 设置卡片重排:总开关回到它控制的一切之前
这一版是账号池卡片的信息架构重排:把「决定整页怎么读」的开关放回标题下方,把每一句说明贴在它所解释的控件旁边,把重复的话删掉。不改任何行为语义——开关仍然是「点保存才生效」,请求参数、落盘格式、配置键、CLI 与宿主范围全部不动。判据移植自同族 dsh-connect-workbuddy(MIT,同作者)的 94c339f:移植的是判据,不是它的 DOM——qoder 独有的三级账号来源(设置 → 环境变量 → 保险库)按本地现实保留。
一、总开关回到它所控制的一切之前
「启用账号池」原来排在设置卡片里,而设置卡片在成员表之后——于是这个决定整页怎么读的开关,排在它所控制的一切下方约 400px,还排在每一列含义都随它变化的成员表之后(「未入池」只在池负责挑号时才成立)。
现在它是标题正下方的一条模式条:开关 + 运行中的模式(「自动挑号」/「手动选号」)+ 未保存标记。
- 模式芯片读的是运行态(已保存的偏好),不是草稿。开关显示「你正要改成什么」、脏标记说「还没保存」,那这枚芯片必须回答另一个问题——「池子此刻在做什么」。读草稿会在你拨了开关还没保存时写着「自动挑号」,而保存着的池其实还关着:显示 A、实际记 B。
- 「谁在服务」只留一个槽位:池关着是选号下拉(搬进标题行右侧),池开着是「当前使用」芯片。同一个账号名此前在一屏上出现三次。qoder 的「自动」是真实第三态(三级回退),不是占位项,这一处没有照抄。
- 池开着、正在计费的号却不在成员名单里时,模式条下多一句旁注——「在用」与「在池里」是正交的两件事,同框会被读成 bug,解释只在这个状态出现。
二、就近:说明、禁用原因、保存动作都搬回它们作用的地方
- 批量动作条搬进成员区(标题之后、账号表之前)。条上每个按钮都只对「已选 N / M」声明的集合做事,而那个数字在上一行、被计数的复选框在下一行。禁用原因(「勾选至少一个账号,才能签到与测试」)紧贴两颗被禁用的按钮。
- 保存条钉在视口底部(
position: sticky)。面板比一屏高,而让任何编辑真正生效的两颗按钮原本在它最底部——你改完的那一刻正好在视口之外。 - 「PAT 从哪来」合成一块,放在添加 PAT 对话框的输入框下方:路径说明与文档外链此前分居两处,各读一半才是完整答案。
- 「更多设置」分节线:过这条线,页面从「池是什么」转到「改它的旋钮」。
三、删掉的重复与顺带修掉的缺陷
- 成员行不再复述「已在池中」:勾选框已勾、行底色已标出的行再写一遍是同一件事说第三遍。缺席的那一半才需要文字——空复选框配空的积分列,否则与「勾了但还没数据」分不开。
- 表下那条琥珀横幅删掉:同一个事实已经写在按钮旁边,300px 之下再来一份更响的副本,只会让页面看起来在两处坏掉。
- 工具条不再自己念一遍标题(「添加账号」压着同名按钮、「导出 / 导入账号」压着「导出」「导入」)。标题保留在
aria-label上,屏幕阅读器照样听得到。 - 桌面宽度下按钮被压成竖排(操作列只有 .88rem 宽,中文可在任意两字之间断行)——加了不换行并收窄内边距。
- 一条空转的测试:旧断言取的是
indexOf('dsm-qoder-bar-label'),而那个标签已被删掉,indexOf恒为 -1、断言恒真——它自那时起就一直假绿。已改为按对话框内真实节点顺序断言。
新增 tests/pool-architecture.spec.tsx(11 条)守住这些位置:它直接挂载真实组件,因为「位置搬错」不会让界面渲染失败、也不会让类型检查失败,只有挂载能抓住。
完整逐条记录见 CHANGELOG.md。升级方式照旧:更新插件后重启一次 DSH,新卡片即生效。
v1.7.3 — 设置卡片不再空白,批量 405 不再把你引向错误的修复
🩹 v1.7.3 — 设置卡片不再空白,批量 405 不再把你引向错误的修复
这一版全是客户端健壮性修复,移植自同族 dsh-connect-workbuddy(MIT,同作者)——qoder 的卡片本就复刻自 workbuddy,上游修过的坑不该在下游重踩。三处都表现为「界面安静地坏了」:没有红横幅、控制台也干净,只是卡片存不了设置、或提示你做一件根本无效的事。宿主侧一行没改。
一、设置卡片整页空白,且没有任何报错
症状:插件列出来是 running,描述和组件行都在,但配置卡片正文一片空白;控制台干净,没有任何错误。
成因:宿主的 settings 镜像由异步 remote.settings.describe() 填充,插件 apply 那一刻它通常还是空的。旧代码把这份「空」当成「我声明的命名空间名一定对」,绑了一个 forms.get(声明名) 之后再也不回看;而宿主按命名空间精确匹配,于是这个猜错的 form 永远停在 unavailable——用户看得见卡片,却存不了任何设置。
修法:改成订阅宿主的 whileServed,等命名空间真正到货才绑定,并重建全部槽位注册(先 dispose 再注册——渲染器按 entry 身份缓存 inject 结果,旧注册留着就一直发旧 props)。即使边缘永不触发(宿主没挂可配置项、settings 面答得晚),也无条件先注册一次:卡片没有 form 也能按只读渲染,那总比不注册强。宿主那条线没有 whileServed 时回退成单次绑定。
顺带修掉两个移植时走样的地方:两个 composer 读数槽漏了 return,disposer 从不被追踪(fiber 拆除时泄漏、重绑时撞「already has an entry」被静默吞掉,读数永远停在第一代闭包);命名空间候选表只监视一种拼写,宿主以 include: 前缀挂载时边缘永不触发、兜底成了死代码。
二、批量接口 405 的误导文案
症状:账号池的签到 / 测试 / 移除返回 405 时,卡片说「请确认插件是最新版本后重试」——可插件已经是最新的。
成因:那种 405 根本不是本插件拒绝该方法,而是宿主进程还在跑更旧的构建、那条路由从未注册,宿主的 HTTP 层在为一个不存在的路由作答。旧文案把用户引向「升级插件」这条走不通的路,真正该做的是重启 DSH。
修法:每条池路由的 handler 都以 JSON 作答(含自身的 405),所以「405 且响应体不可解析」就是宿主路由缺失的指纹。命中时改提示重启应用,并明确否定「重装或升级插件」。以 JSON 作答的 405 仍是「方法不对」,别的状态没有 body 也照旧措辞。
三、这一版的测试
两条改动此前零覆盖,这次补了 17 条。其中 9 条直接 import 真实的 apply()——上游那边导入真实入口会拉进浏览器专属的宿主包,只能测手抄的镜像副本(会随产品漂移而静默失效);qoder 侧实测可以直接 import,所以断言的是产品代码本身。
六个变异逐个跑过验证测试真抓得住:加回早期猜测绑定、去掉两个 return、候选表缩回单一拼写、重绑顺序倒置、去掉 405 限定、去掉 bodyUnreadable 判据——每个都至少一条用例变红。
完整逐条记录见 CHANGELOG.md。升级后请重启一次 DSH(宿主进程需要重新注册路由)。