dsh-skill-cards
Verifieddsh-skill-cards · v1.1.0 · MIT · Web UI
技能卡片 — 在聊天输入框下方与「设置 → 技能」各放一个入口,点开是以图片卡片呈现的技能浏览弹窗,支持自定义封面图、单选/多选,选中后仅对下一条消息生效,发送即自动移除。Skill Cards for DeepSeek Harness.
Install
dsh plugin add dsh-skill-cards Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Creators
Readme
🎴 dsh-skill-cards · 技能卡片
把 DeepSeek Harness 的技能目录变成可交互的扇形游戏王卡组。
以图片卡片浏览本机技能 · 自定义封面与卡名 · 智能分类 · 墙 / 扇形双布局 · 抽卡揭示 · 一键注入当前会话
一个 DeepSeek Harness 插件。在聊天输入框下方放一个「技能」按钮,同时在设置 → 技能里内嵌完整管理器,点开是一个以图片卡片呈现的技能浏览弹窗。选中技能后仅对下一条消息生效(发送即自动移除)——不动磁盘、不污染全局目录。
📸 效果
| 技能管理 | 卡牌浏览(墙 / 扇形) | 抽卡揭示 |
|---|---|---|
| 分类筛选 / 搜索 / 多选 / ★ 常用 | 扇形:中央放大 + 两侧弧形展开 + WebGL 星云背景;墙:4/3/2 列网格 + 悬停 3D 倾斜 | 应用后卡背飞入 → 逐张 3D 翻面 |
✨ 功能一览
两个入口
- 聊天输入框下方「技能」按钮 — 注册在官方
conversation.input.right槽位(输入框下方、发送按钮左侧),走官方槽位而非 DOM 探测,稳定可靠。 - 设置 → 技能 — 整个管理器内嵌在该设置项内(
settings.section),点开即用,无需再开弹窗。
两个入口都是会话级槽位,宿主会把所属会话的 sessionId 直接传给它们,因此多窗口天然隔离——在 A 窗口应用的技能不会出现在 B 窗口。
插件入口(DSH 自带的插件管理面板)不做任何改动——本插件只管「技能的可视化浏览与按需引入」。
🎴 游戏王式卡面(按分类自动换装)
- 卡面为 TCG 结构:金属外框 + 属性徽章 + 名字条(星级)+ 插画窗(封面图优先,无图则显示名字缩写字纹)+ 描述框 + 右下 ATK 数值(= 使用次数)。
- 主题表驱动:11 个内置分类各有专属框色 / 插画渐变 / 辉光(开发·橙、测试·绿、设计·紫、安全·红……)。自建分类查表不到时按名字哈希生成色相(饱和度/亮度锁定在卡框区间),任何后续新增分类都自动有一套和谐皮肤。
- 闪卡罕贵度:约 1/8 的技能(名字哈希确定性)为 SR 卡,卡面带持续流动的斜向碎闪光泽,悬停任意卡也会扫光。无随机抖动 —— 同名技能每次都是同一个罕贵度。
🧊 扇形卡牌浏览(默认布局)
- 打开就停在卡组正中:当前浏览卡位于中央、尺寸最大、层级最高,左右各有邻牌、逐格对称,所以第一眼就是完整扇形,两个方向也都能立刻走。两侧卡牌逐渐缩小并围绕底部中心旋转,保留可点击的重叠区域。点击侧牌会平滑移至中央;点击中央卡面或下方「勾选技能」切换业务勾选,浏览不改变已有多选。
- 三种翻牌方式,都跟手:① 左右拖动卡牌 —— 鼠标与触屏都支持,卡面逐帧跟着指针走、松手自动吸附到最近的卡位(一次拖动最多带走 3 张,避免为了跟随而挂载整副卡组);② 点中央卡两侧的空白 —— 点哪一边就往那一边翻一张;③ 左右按钮,或聚焦卡牌区域后按 ← / →。首尾顶住、不循环跳转;中央卡下方显示当前名称、完整描述及勾选、常用、自定义按钮。
- 宽面板最多显示 7 张、挂载 9 张(含透明过渡缓冲卡,拖动时按跟随方向再扩几张);窄容器自动减少数量。布局按实际面板宽度计算,保留已有卡面、封面素材和分类主题。
- 卡组区(窗口偏矮或描述较长时)自己滚动但不显示滚动条 —— 那条贴右内沿的滚动条会割裂卡面与细节区;滚轮 / 触控板照常,聚焦卡组区后方向键、PageUp·PageDown、空格也能滚。
- 背景是 WebGL 粒子星云(three.js,随鼠标轻微移动,跟随明暗主题)。
- 降级行为:WebGL 不可用 / three.js 加载失败时仅隐藏星云,仍使用扇形布局;系统「减弱动态效果」关闭切换等动态效果并跳过抽卡动画,布局与搜索、多选、改图、应用功能保留。关闭弹窗即释放 GPU。
- three.js 不在 DSH 客户端模块表里(
require('three')会抛错),所以插件自带一份 UMD 版(vendor/three.min.jsv0.152.2),由宿主端以同源经典<script>提供 —— 不走 CDN,离线可用。
🔀 布局切换(墙 / 扇形)
- 默认扇形:没设置过偏好时打开就是上面的扇形浏览。工具栏右侧的 「墙 / 扇」分段按钮(两个真按钮,带
aria-pressed)可随时切换。 - 墙 = 网格卡墙:4 / 3 / 2 列响应式铺开全部卡片,悬停有 3D 倾斜,进入墙模式时播一次入场波浪(之后不再重播)。墙模式面板高度随内容自适应,3 张卡不会留 82vh 空白。
- 偏好落盘在宿主端:写在
~/.dsh/skill-cards-stats.json的保留键_prefs({ "layout": "fan" | "wall" }),跟分类 / 封面一样属于用户数据,可被现有备份 / 迁移流程覆盖。走GET/POST /plugins/dsh-skill-cards/prefs;客户端另用localStorage['dsh-skill-cards.layout']做首屏镜像,避免读到权威值前闪一下默认布局。 - 两个入口都生效:浮层与「设置 → 技能」共享同一个面板组件,切换一处、另一处跟着变;偏好是用户级的,换窗口、重启 DSH 都保持。
- 切换不丢状态:搜索词、分类筛选、多选全部保留;并保留当前浏览的那张卡 —— 切到墙会把它滚进视口并短暂描边,切回扇形以它居中。
- 滚动条策略:两种布局的卡组区(
.dsh-sc-stage)都不显示滚动条,但滚动能力保留(滚轮 / 触控板 / 聚焦卡组区后的方向键与 PageUp·PageDown)。 - 非法值不落盘:
POST /prefs收到白名单外的layout返回 400unknown layout,存储保持原值;GET /prefs总是返回补齐默认值的完整对象。
🗂️ 分类 / 常用 / 使用次数
- 每张卡按名称与描述自动归类到 11 个中文分组:开发 / 测试 / 设计 / 写作·文档 / 办公文档 / AI·智能体 / 流程·工程 / 研究·分析 / 技能·管理 / 安全 / 数据。
- 顶部分类筛选 tab(全部 / 常用 / 各分组,带计数)+ 右侧下拉框(默认 / 按使用次数 / 只看收藏)双通道筛选;★ 常用卡片置顶。
- 分类管理:底部工具栏独立「分类管理」按钮可新增 / 重命名 / 删除用户自建分类。内置智能分类只读,自建分类自由编辑,新增分组会并入筛选与卡面主题。
- 卡面布局:标题 ≤2 行、描述 ≤6 行(卡面字号)、分类固定左下角、使用次数固定右下角。
- 中央卡稳定后,卡面左上角工具栏在悬停时出现:
★ 常用 · 译 中文化 · ↺ 还原 · 原 原文 · 🎨 卡图 · 自定义,一排在宽度不够时自动换行,任何宽度都不会被裁掉或互相压住。 - 点中央卡左下角的分类徽章弹出分类菜单(列出全部内置 + 自建分类)。菜单会自动判断上下翻转并限高滚动 —— 卡片贴近滚动区底部时朝上展开,分类再多也能一路选到最后一项,不会被相邻卡片盖住、也不会被裁掉。
🔍 查看技能原文(「原」按钮)
- 悬停卡面左上角的工具栏,点其中的**「原」(在 ★ 译 ↺ 之后、🎨 之前),弹出该技能的原始信息**:原始技能名、原始完整描述、来源目录。
- 为什么需要它:卡面会中文化(「译」),描述也被裁剪成 6 行卡面字号 —— 翻译或超长描述之后,技能安装时原本写的是什么就看不到了。这里给的是未经翻译/未经改写的原文,可选中复制。
🖼️ 自定义封面 / 卡名
- 悬停卡面左上角的工具栏,点其中**「自定义」(工具栏最后一项,唯一带文字的按钮),可填写封面图 URL、标题、描述**;也可从本机选图上传。
- 上传的图片作为文件存到
~/.dsh/skill-cards-images/,配置里只存受保护的同源服务 URL —— 不存 base64,防止配置被撑爆。支持png / jpg / webp / gif / svg。 - 落盘于
~/.dsh/skill-cards.json,刷新即生效。
🎴 应用到下一条消息(抽卡仪式感)
- 点「应用到本次会话」:弹窗先关闭,暗金纹章卡背依次飞入屏幕中央,再逐张 3D 翻面揭示(间隔 420ms),SR 卡翻面瞬间金光爆闪。点击任意处 /
Esc可跳过;系统「减弱动效」时直接跳过。 - 成功引入的技能名以彩色标签出现在输入框「技能」按钮上方(约 4 秒后消失)。
技能是怎么生效的
技能不是随消息一起发送的,也不会写进输入框。点击「应用」时,宿主端把它们注册到当前会话的 skills 服务上:
skills.register({ name, description, content, resourceBase, invocation, source })
所以从这一刻起,该会话的 agent 就一直能看到它们 —— 你发什么内容都会用上。标签只是状态展示,不是待发送的内容,不存在「重复发送」。
会话被关闭时,宿主会收到 agent-removed 事件并自动注销这些技能。
一次性:发送后自动移除
「应用」始终是一次性的。 你选中的技能只服务于下一条要发的消息:按下回车时它们被自动注销,标签随之消失。下一条消息不会带上这批技能。
这正是本插件与「会话级常驻」的区别 —— 技能是逐条消息按需装载的,不会在你看不见的地方长期影响后续对话。
若想提前手动移除,点标签上的 × 或面板里的「全部移除」即可。
实现说明:DSH 未对外暴露「消息已发送」事件(其提交状态机
begin-submit/send-committed/commit-draft均为dsh-client-ui-conversation的内部实现)。而宿主只在submit-settled且ev.ok后才清空编辑器——即应答被接受之后,且回合进行中可能保留原文——所以「草稿变空」这个信号既滞后又不可靠。最终采用宿主自身会响应的两个信号:捕获阶段的
keydownEnter 与点击主按钮 —— 两者在宿主内部都归到同一个keyboard.submit(mode, …)入口。
- Enter:监听宿主输入框标记
div[role=textbox][data-composer-input],过滤Shift+Enter与输入法组合中的 Enter。必须在捕获阶段,因为宿主的键盘映射会在 Enter 上终止事件传播,冒泡阶段的监听器永远收不到。- 发送按钮:宿主没给它 data 属性,且发送与停止按钮共用同一个
primary类。因此以插件自己的按钮为锚点判断是否位于同一输入栏,再按primary类或发送文案匹配,并排除停止/取消类文案 —— 取消回合不算提交。
🌏 技能中文化
- 底部工具栏「中文化」打开批量面板:① 生成提示词并复制(列出所有未翻译技能)→ 粘到对话框发给 agent → ② 把返回的 JSON 粘回面板点「写入」。
- 卡面左上角工具栏里的「译」按钮可单卡翻译。
- agent 也可直接调接口写入(免粘贴):
curl -X POST http://127.0.0.1:19387/plugins/dsh-skill-cards/i18n \
-H 'content-type: application/json' \
-d '{"items":[{"name":"tdd","zh":"测试仪式","zhDesc":"红-绿-重构循环的守护者。"}]}'
- 翻译优先渲染中文名,无翻译则回退英文原名 —— 没翻译的技能照常显示。未知技能名会被服务端跳过,不会污染配置。
- 中文化只影响卡面:想看技能原本的名字与描述,点卡面左上角工具栏里的**「原」**(见上一节),随时对照原文。
📦 安装
[!IMPORTANT] 务必走官方安装器,不要手改 profile 的
package.json/node_modules。 DSH 桌面版把这四者当作一个原子事务;只改其中一部分会让 DSH 启动时判定组合不一致,并写入最小安全配置(表现为:其它插件全部消失、配置被重置)。
桌面版
# 桌面版必须用安装目录自带的 CLI,普通 dsh CLI 会被 Electron 独占拒绝
"/Applications/DeepSeek Harness.app/Contents/Resources/runtime/cli/bin/dsh" plugin --profile desktop add dsh-skill-cards
或直接用应用内 设置 → 插件管理 → 添加。
Web 版
dsh plugin --profile web add dsh-skill-cards
从源码安装(贡献者)
git clone https://github.com/baobao1994/dsh-skill-cards.git
dsh plugin --profile web add "/absolute/path/to/dsh-skill-cards"
安装是纯 insert 的 cordis.patch.yml。若宿主代码未加载(弹窗打不开),重启一次 DeepSeek Harness;之后 client/ 的改动刷新页面即可生效。
卸载
"/Applications/DeepSeek Harness.app/Contents/Resources/runtime/cli/bin/dsh" plugin --profile desktop remove dsh-skill-cards
🚀 使用
- 打开面板 — 点输入框下方的「技能」按钮,或进 设置 → 技能。
- 筛选与搜索 — 顶部分类 tab + 搜索框(同时匹配中英文名与描述)。
- 浏览 — 打开就停在卡组正中的那张。左右拖动卡牌(鼠标 / 触屏,跟手、松手吸附)、点中央卡两侧的空白、点侧牌直接居中、左右按钮或 ← / → 都能翻牌。中央卡下方显示完整名称、描述及操作按钮。工具栏右侧的 「墙 / 扇」 可切换到网格卡墙;两种布局都保留搜索、筛选与多选,切换时还会保留当前浏览的那张卡。
- 定制 — 中央卡稳定后,悬停其左上角工具栏:★ 设常用 · 译 中文化 · ↺ 还原 · 原 看原文 · 🎨 卡图 · 自定义 换封面与文案;点左下角分类徽章改归类。
- 勾选 — 点中央卡面或详情区的勾选按钮切换选中,右上角复选框显示状态,可跨卡牌多选。浏览切换保留勾选;工具栏可全选当前筛选结果 / 清空。
- 应用 — 点「应用到本次会话」,看抽卡揭示动画,确认技能已进入本会话。
- 随时撤销 — 再次打开面板,已应用的技能带标记,再点一次即从本会话移除。
技能从哪来
插件不提供技能市场,只展示本机已安装的技能。列表为空时,把技能目录放到以下任一位置后刷新:
| 位置 | 说明 |
|---|---|
~/.dsh/skills/ |
DSH 用户技能根目录 |
~/.agents/skills/ |
Agent CLI 安装位置(主力) |
~/.claude/skills/ |
Claude 兼容技能池 |
<项目>/.dsh/skills/ |
项目级 |
<项目>/.agents/skills/ |
项目级 |
每个技能是一个直接包含 SKILL.md 的目录(或扁平的 <name>.md)。$DSH_HOME 可覆盖默认的 ~/.dsh。
技能放在别处?
设置环境变量 DSH_SKILL_CARDS_ROOTS 指向任意目录,可同时指定多个:
# macOS / Linux 用 : 分隔
export DSH_SKILL_CARDS_ROOTS="/opt/skills:$HOME/dotfiles/agent-skills"
# Windows PowerShell 用 ; 分隔
$env:DSH_SKILL_CARDS_ROOTS = "D:\skills;E:\dotfiles\skills"
与默认路径重复的项会被自动忽略,放心列出。
列表为空?先看扫描报告
列表为空时,面板会直接列出本次实际扫描过的每个目录及其命中数量 —— 不存在的目录标红删除线。这样能立刻区分「目录不存在」和「目录存在但没有有效技能」:
本次扫描的目录:
~/.agents/skills 无有效技能 · 0
~/.claude/skills 不存在 ← 红色:这个目录压根没有
~/.dsh/skills 无有效技能 · 0
技能名支持小写字母、数字、-、_、. 以及大写字母 —— GitHub、TDD 这类来自其它 agent 的命名不会被静默丢弃。
同一技能出现在多个目录时只显示一次,优先级为 DSH 用户目录 > .agents > .claude > 自定义 > 项目级。
封面图自动识别
在技能目录(含 SKILL.md 的那一层)放下列任一文件名,即自动作为卡片插画:
cover.png cover.jpg cover.jpeg cover.webp cover.svg preview.png preview.jpg
想用任意图片,请用卡片上的「自定义」按钮上传,或填 http(s) 图片链接。
🔌 HTTP API 参考
所有路由挂在 /plugins/dsh-skill-cards/ 下,并做同源校验(跨域请求返回 403)。仅本机 DSH 使用,无鉴权需求,请勿反向代理暴露到公网。
GET
| 路由 | 说明 |
|---|---|
skills?cwd=<path> |
已安装技能列表 + 合并后的分类表 + 扫描报告(每个根目录的 exists / found) |
cards |
全部自定义卡面配置 |
stats |
分类 / ★ 常用 / 使用次数 + 自建分类 |
prefs |
用户 UI 偏好(当前只有 layout:fan | wall),总是补齐默认值 |
cover?skill=<name> |
技能目录内的封面图(无则 404) |
image?id=<hash>.<ext> |
上传图片回读(id 需匹配 [a-f0-9]{24}\.(png|jpe?g|webp|gif|svg)) |
active-session |
当前活跃会话 id |
applied?sessionId=<id> |
该会话已引入的技能名列表 |
vendor/three.min.js、vendor/card-back.jpg |
插件自带的静态资源(白名单) |
POST
| 路由 | body | 说明 |
|---|---|---|
introduce |
{ sessionId?, names: string[] } |
把技能注册进会话级 skills 服务 |
revoke |
{ sessionId?, names: string[] } |
从会话移除已引入的技能 |
cards |
{ name, image?, title?, description?, i18n? } |
写入卡面配置;image: '' 清除,i18n: null 清除中文层 |
i18n |
{ items: [{ name, zh, zhDesc? }] } |
批量写入中文卡名 |
stats |
{ name, category?, starred? } |
局部更新某技能统计 |
prefs |
{ layout: "fan" | "wall" } |
写入 UI 偏好;白名单外的值返回 400,且存储保持原值 |
categories |
{ action: "add"|"rename"|"remove", id?, label? } |
分类管理 |
image |
raw body + content-type: image/* |
上传封面图,返回受保护的 URL |
⚙️ 工作原理
- 技能来源:宿主端 lib/skills-scan.js 扫描上述技能根,解析
SKILL.md的 YAML frontmatter(name/description,支持行内、引号、块标量三种真实写法),并探测同目录封面图。 - 会话级引入:
POST /introduce→ctx.get('agents')?.get(sessionId)取出当前会话的 agent → 对其会话作用域的skills服务调用register({ name, description, content, resourceBase, invocation, source })。纯内存注册,重启前一直有效,不写任何技能目录。 - ** disposer 保留**:每次注册返回的 disposer 都存在
sessionId → skillName → dispose的注册表里,因此技能可以被撤销;agent 移除时该表整块清理,避免出现「幽灵技能」。 - 当前会话判定:两个入口都是会话级槽位(
scope: "session"),宿主把所属会话的sessionId作为标准 props 直接传入。客户端优先取它,并在每次/applied、/introduce、/revoke时显式带上。宿主是单进程、被所有窗口共享的,所以任何「记住最近一次会话」的做法都会跨窗口串味——早期版本正是如此:A 窗口应用后,B 窗口因为没带 id 而拿到 A 的技能。会话 id 必须随每个请求传递,绝不留在宿主端的全局字段里。
- 数据落盘:
| 文件 | 内容 |
|---|---|
~/.dsh/skill-cards.json |
卡面配置(封面 URL / 标题 / 描述 / 中文层) |
~/.dsh/skill-cards-stats.json |
分类 / ★ / 使用次数 / 自建分类 / UI 偏好 _prefs(与配置分离,高频写不搅动布局) |
~/.dsh/skill-cards-images/ |
上传的图片原始字节(按 sha256 前 24 位命名) |
客户端加载契约(给二次开发者)
window.__ModuleLoader__.load({ id, factory }) 的 factory 只接收 require 一个参数,DSH 不会传入 module / exports。必须自己造:
factory: (require) => {
const module = { exports: {} }
const exports = module.exports
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' })
// ...
exports.name = 'dsh-skill-cards'
exports.inject = ['slots', 'locale']
exports.apply = apply
return exports
}
写成 factory: (require, module) => { const exports = module.exports } 会在启动时抛
Cannot read properties of undefined (reading 'exports'),导致 web boot: 1 entry did not activate,进而触发上面的配置重置。
🗂️ 目录结构
dsh-skill-cards/
├── package.json # 插件清单(no-build,web 客户端)
├── cordis.patch.yml # 纯 insert 补丁,热挂载
├── vendor/
│ ├── three.min.js # three.js v0.152.2 UMD(星云背景,同源经典 <script> 加载)
│ └── card-back.jpg # 卡背雕花纹理
├── lib/ # ── 宿主端(Node)
│ ├── index.js # HTTP 路由 / vendor 静态文件 / 封面服务 / 会话引入
│ ├── skills-scan.js # 枚举本机技能 + 解析 frontmatter + 探测封面
│ ├── stats.js # 分类 / 常用 / 使用次数 + 智能分类建议
│ └── http.js # HTTP 辅助(sameOrigin 闸门 / JSON / 文件)
├── client/
│ └── client.js # ── 浏览器端:入口按钮 + 设置面板 + 卡片浮层 + 墙/扇形双布局 + 出卡
├── diagnose/ # ── 脱离 DSH 的自测页(真实 React 渲染)
└── test/ # ── node:test 单测(122 个)
🛠️ 开发
npm install # 无运行时依赖,仅为 diagnose 页拉取 React UMD
npm test # node --test,122 个用例
npm run check # 对所有源码做 node --check 语法校验
PORT=43691 npm run diagnose # 启动独立自测页 http://127.0.0.1:43691/diagnose/
若测试运行器只输出文件级结果,可逐个执行以查看子测试:for file in test/*.test.mjs; do node "$file" || exit; done。
架构约束(改动时务必遵守)
- 宿主端只
inject = ['webServer']。skills是 agent 级服务,在根作用域 await 它会让 Cordis 的apply()永久挂起 —— 症状是按钮出现了但所有请求 404。需要agents/skills时用ctx.get()机会式读取。 - 路由前缀不带尾部斜杠。宿主用
pathname === prefix || pathname.startsWith(prefix + '/')匹配前缀路由,带斜杠会导致静默全量 404。 - 绝不把 base64 写进配置。图片以文件存储,配置只存同源 URL。
client/client.js不走构建。JSX 需手工替换为React.createElement,CSS 全部内联。- 只注入到会话作用域。任何时候不要注册到宿主全局 skills 服务,否则会泄漏到所有会话。
❓ 疑难排查
| 症状 | 原因 / 解决 |
|---|---|
| 输入框下方没有「技能」按钮 | conversation.input.right 是会话级槽位,尚未进入会话时不渲染。改从 设置 → 技能 进入 |
| A 窗口应用的技能出现在 B 窗口 | 已修复(1.0.2)。两端都不会「记住」会话,id 随每个请求传递 |
| 分类菜单被旁边的卡片盖住 / 分类多时选不到最后几项 | 已修复(1.0.3)。菜单现在会抬到相邻卡片之上、按剩余空间限高滚动,并自动朝上/朝下展开 |
| 中文化后看不到技能原本的名字和描述 | 已修复(1.0.3)。卡面左上角工具栏里的「原」按钮可查看未翻译的原文(原始技能名 / 完整描述 / 来源) |
| 想换回网格卡墙(扇形之前的布局) | 工具栏右侧的「墙 / 扇」分段按钮随时可切,偏好会记住(宿主端 _prefs,见布局切换)。默认是扇形 |
| 切牌时卡片底部的分类标签闪动 | 已修复(1.1.0)。标签改为恒挂载、过渡期只摘掉交互能力,不再卸载重挂,.gc-stats 行不再重排 |
| 卡片列表为空 | 技能没放在已识别的技能根目录(见上文表格)。面板会列出本次扫描过的目录,注意看哪个标红 |
| 卡片没有插画 | 目录内没有 cover.* / preview.* 文件。用卡面左上角工具栏里的「自定义」按钮上传或填 URL |
| 没有星云或切换动画 | WebGL 不可用 / three.js 加载失败会隐藏星云;「减弱动态效果」会关闭动画。扇形布局与操作仍然保留 |
| 弹窗一闪而过 / 设置空白 | 先跑 PORT=43691 npm run diagnose 打开 http://127.0.0.1:43691/diagnose/,看诊断日志两行是否都是 ✅。❌ 则把红字与 F12 Console 报错反馈到 Issues |
| 装上后其它插件全没了 | 手改了 profile 的 package.json。用官方安装器重新添加,见安装 |
| 升级后改动没生效 | pnpm 因 ^版本号 已满足而静默跳过(输出 Already up to date)。必须先 remove 再 add,见 CONTRIBUTING |
单独自测 client.js
如果「点击技能弹窗一闪而过 / 设置空白」,先在真实 DSH 之外验证插件自身是否健康:
PORT=43691 npm run diagnose→ 打开 http://127.0.0.1:43691/diagnose/。- 看「诊断日志」:两行都应是 ✅、无红色 ❌。
- ✅ → client.js 在真实 React 下渲染正常 → 问题在 DSH 宿主侧。
- ❌ → 把红字与浏览器控制台(F12 → Console)报错反馈回来。
诊断页支持 ?count=0、?count=1、?count=2 和 ?count=95,分别检查空结果、单卡、首尾边界和大卡组。例:http://127.0.0.1:43691/diagnose/?count=95。检查「打开即在正中且左右对称」「鼠标 / 触屏拖动跟手、松手吸附」「点两侧空白翻页」「侧牌点击」「中央勾选」「滚动条隐藏但可滚」「窗口缩放」「详情与分类弹层」「两种布局各自可用、切换后偏好保持(宿主端 _prefs)」,并确认拖动、点空白都不会误勾选。想要自动化回归,可复用本地脚本 .shots/fan-check.mjs(headless Chrome + CDP,零依赖,文件头注释里写了启动命令;.shots/ 已 gitignore,不入库)。断言数随脚本增长,以脚本内的 check() 调用为准。
该页自带本地 React 副本,只需一次 PORT=43691 npm run diagnose,无需联网。
🤝 贡献
欢迎 PR!提交前请确保:
npm run check && npm test
- 新增功能同时补
test/下的用例(当前 122 个)。 - 遵守上方 架构约束 五条,尤其是会话作用域与路由前缀。
- UI 改动请在支持 / 不支持 WebGL、以及「减弱动态效果」三种情形下各验一次。
- UI 文案保持中英双语(
locale.register的 zh / en 两份都要加);README 目前只维护中文版。
完整流程见 CONTRIBUTING.md —— 里面记录了本项目特有的加载契约、会导致静默失效的坑,以及发版注意事项。
📄 许可
MIT © 2026 baobao1994
本插件与 DeepSeek 官方无隶属关系。技能内容由各自作者所有。