DSH Web 的快捷控制面板:可停靠或浮动,开关项可由其它插件通过 quickControl 服务注册;内置主题/皮肤切换与细粒度 NO_PROXY 代理控制,可搭配 dock-base 或独立运行。
安装
# Release 预构建包
dsh plugin --profile web add "https://github.com/tcgbp/dock-flash/releases/latest/download/dock-flash.tgz"
# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)
dsh plugin --profile web add github:tcgbp/dock-flash
装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本——pnpm 默认拦截,所以安装可能停在 ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED 或 ERR_PNPM_IGNORED_BUILDS;dsh 会打印出需要添加的确切键名,把它加进该 profile 的 pnpm-workspace.yaml 的 allowBuilds 下,重跑一次即可装上。放行构建本身就是一次信任判断:请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。
README
DSH 快捷控制插件 —— 可独立运行或配合 dock-base 使用。点击⚡图标打开快捷控制悬浮窗口,支持其他插件动态注册快捷开关。
运行模式
| 模式 | 条件 | 界面 | 可用开关 |
|---|---|---|---|
| 工作台模式 | 已安装 dock-base | ⚡ 图标在活动栏 → 侧边栏/浮窗面板 | 外观 + 系统开关 |
| 独立模式 | 未安装 dock-base | ⚡ 触发按钮注入所选会话槽位(默认:输入框右侧)→ 浮动面板 | 外观 + 布局 + 系统 |
功能
内置开关
内置开关按外观 / 布局 / 系统三组分类显示(紧凑双列布局)。其中「布局」组仅独立模式会出现:
| 分组 | 开关 | 类型 | 说明 | 独立模式 |
|---|---|---|---|---|
| 🎨 外观 | 外观 | select | 浅色 / 深色 / 跟随系统,切换 DSH 全局外观(含 wxj-black-hole 主题冲突重试机制) | ✅ |
| 🎨 外观 | 皮肤 | select | 动态扫描已安装皮肤插件并切换(需要 dsh-market) | ✅ |
| 🎨 外观 | 时间线移到左侧 | toggle | 把 DSH 自带的轮次导航栏从右侧挪到左侧。仅当屏幕上显示的是 DSH 自己的轨、且没有别的 timeline 插件接管时才出现 | ✅ |
| 🎨 外观 | 全屏 | toggle | 浏览器 Fullscreen API,全屏/退出全屏 | ✅ |
| 🎨 外观 | 日志下载按钮 | toggle | 显示/隐藏会话日志下载按钮 | ✅ |
| 📐 布局 | 失焦关闭 | buttongroup | 关闭 / 开启,点击面板外部时自动关闭浮窗。标题栏上也有同一开关(两种模式) | ✅ |
| 📐 布局 | 触发位置 | select | 输入框左 / 输入框右 / 会话标题栏操作区 / 会话标题栏工具区 —— 独立模式 ⚡ 触发按钮的位置;或「对话区右上角」—— 浮在对话区内的按钮,可拖动到区内任意位置 | ✅ |
| ⚙️ 系统 | 语言 | buttongroup | 中文 / English,切换 DSH 全局 UI 语言 | ✅ |
| ⚙️ 系统 | 系统代理 | select | 全部代理 / 仅 API 绕过 / 全部绕过 / 自定义 — 细粒度 NO_PROXY 控制。自定义会校验取值:主机名 / 域名后缀 / IP / 主机:端口,逗号或空格分隔;空值与 CIDR 会被拒绝 | ✅ |
| ⚙️ 系统 | 测试 URL | select | Google 204 / GitHub / DeepSeek API / 自定义 —— 测试连接所探测的地址,并在下拉框下方单独一行显示当前生效的 URL(自定义地址因此可见)。归属代理簇,可折叠收起 | ✅ |
| ⚙️ 系统 | 诊断日志 | log | 只读多行日志,显示最近一次测试结果 —— 一行一件事。按下「测试连接」的瞬间即出现,首行就是正在尝试的地址,随后被完整报告替换(从不追加);点 ✕ 清空,不受 30 秒 TTL 限制 | ✅ |
「布局」分组只在独立模式出现。 集成模式下
close-on-blur只以面板标题栏按钮的形式存在,因此没有任何内置开关带group: 'layout',该分类会被整体跳过。
停靠布局不在这里配置。 停靠边、自动隐藏、保留空间、图标缩放都是 dock-base 自己的设置项 —— dock-flash 有意不重复提供。布局分组里 dock-flash 只拥有
trigger-position与close-on-blur,两者都仅独立模式存在。
「时间线移到左侧」只在屏幕上显示的是 DSH 自带那条轨时才出现。 没有会话、会话还没有轮次、被 DSH 自己的
@container (width<=900px)规则隐藏、或已被别的 timeline 插件接管时都不显示 ——dsh-codex-timeline是原地增强原生轨,屏幕上那条属于它,dock-flash 不与它争同一个位置。浏览器控制台执行__dockFlashTurnRail()可打印判定结果与原因。
核心能力
- 🧩 动态发现 — 其他插件通过
quickControl服务注册自己的快捷开关,面板自动渲染 - 🌐 国际化 — 完整的中英文本地化,自动跟随 DSH 语言设置(通过
<html lang>MutationObserver 实时同步) - 🎨 皮肤系统 — 多层发现 + 分类切换(CSS / 托管 / 排除)
- 🛡️ 错误边界 — 所有面板组件包裹在
PanelErrorBoundary中,防止渲染错误崩溃整个 dock-base WorkbenchRoot - 🔌 独立运行 — 无需 dock-base 即可运行:⚡ 触发按钮注入到所选会话槽位,点击展开悬浮面板
- 📝 最近修改 — 自动记录开关操作(30 秒 TTL),以 "旧值 → 新值" 格式展示
- 🩺 连接诊断 — 只读多行日志,显示最近一次代理测试(链路走向、重定向链、耗时、响应体大小、socket 错误码),一行一件事。按下按钮的瞬间即出现(首行是正在尝试的地址),答案返回后被完整报告替换,从不追加
- 🧮 面板排序与隐藏 — 工作台与扩展页签头部的 ⇅ 图标进入排序模式:用 ▲▼ 调整分组与开关的先后,结果保存在 DSH 配置里,换浏览器或换机器都会跟着走。旁边的 ◉ 图标进入显示/隐藏模式:每一行都有一个 ●/○ 勾选框,取消勾选就把不用的开关从面板上收起,但它仍列在这里,随时可以放回来。两个配置页都会列出全部已注册控件,包括插件当前自行停用的那些 —— DSH 轨道不在时的「时间线靠左」、没配代理时的代理探测 —— 这些行会灰显并在悬停时说明原因,所以一时用不上的控件同样可以排序或隐藏。两个模式互斥 —— 进入一个另一个的入口就不再显示 —— 且各自有独立的 ↺ 重置:恢复顺序不会把隐藏的行放出来,恢复显示也不会打乱你的顺序。声明了
cluster的开关整簇作为一个单位移动、簇内顺序固定,并渲染成一张卡片、成员可折叠 —— 系统代理那组就是动因:它们要配置了代理才起作用
Host 端功能
src/index.ts(Host 半)提供:
- 注册
dock-flash设置命名空间(proxyMode字符串 +customNoProxy字符串 +testUrl字符串) - 监听代理模式变更,通过
@deepseek-ai/dsh-http-proxy重新安装 undici 全局 dispatcher,使出站fetch()请求遵循用户设定的 NO_PROXY 规则 - 提供 HTTP 路由:
GET /plugins/dock-flash/proxy-status— 返回当前proxyMode、customNoProxy、testUrl及实际NO_PROXY环境变量值POST /plugins/dock-flash/test-connection— 执行诊断式连通性探测;可选用{ "url": "..." }请求体覆盖已存目标
连接诊断
POST /plugins/dock-flash/test-connection 返回的是一份结构化诊断报告,而不是简单的成功/失败:
| 字段 | 含义 |
|---|---|
proxy |
{ mode, noProxy, httpProxy, proxied, routeError } —— dsh-http-proxy 会如何路由这个具体 URL |
redirects |
重定向链,逐跳手动跟随(redirect: 'manual')记录;超出上限时 redirectLimitHit 为真 |
status / statusText |
最终响应状态 —— 只要能拿到 HTTP 响应就算 ok,因为它已经证明网络通路是通的 |
headersMs / bodyMs / elapsedMs |
响应头耗时、响应体耗时、总耗时 |
bodyBytes / bodySnippet |
响应体大小,以及文本型响应体前 200 字节 —— 企业代理自己的「已拦截」页面就出现在这里 |
error |
{ name, message, code, causeName, causeMessage, causeCode, causeErrno } —— 内层 undici cause 才携带 ENOTFOUND、ECONNREFUSED、UND_ERR_CONNECT_TIMEOUT、DEPTH_ZERO_SELF_SIGNED_CERT 等真实原因 |
面板把这份报告渲染成 诊断日志 区块,一行一件事。测试一开始该区块就出现(首行就是正在尝试的地址),并且只保留最近一次:每次测试都整体替换而非追加 —— 面板很短,堆积的历史会埋掉你刚问的那一次。
测试目标是一个设置项,绝不是一个常量。
testUrl默认https://www.google.com/generate_204,保存在磁盘上的 DSH 配置里,因此内网地址可以配置而不会出现在本仓库中。
代理模式选项
| 模式 | NO_PROXY | 效果 |
|---|---|---|
| 全部代理 | (移除) | 所有流量走系统代理 |
| 仅 API 绕过 | api.deepseek.com,chat.deepseek.com |
DeepSeek API 请求绕过代理 |
| 全部绕过 | * |
所有流量绕过代理(直连) |
| 自定义 | (用户自定义) | 用户通过输入框指定 NO_PROXY 值 |
代理作用范围
此设置仅影响 DSH 进程内的
fetch()请求。
- ✅ 受影响:Node.js 内置
fetch()(undici)、DSH API 调用、MCP HTTP 传输层、pi-ai provider 及所有经过globalThis.fetch的 SDK- ❌ 不受影响:通过
node:http/node:https模块发出的请求(如 OTLP 遥测)、自建传输层的 SDK(如 E2B)、操作系统的其他应用程序、浏览器或其他终端会话- 此设置在 Windows、macOS、Linux 上行为一致 — 修改的是
process.env和 undici 全局 dispatcher,均为 Node.js 抽象层,无操作系统差异
结构
src/index.ts HOST 半 — 设置命名空间 + 代理模式 + 连接测试(tsc → dist/)
lib/client.js BROWSER 半 — quickControl 注册表 + 动态面板 + 皮肤系统 + i18n
cordis.patch.yml bundle layer — 将宿主行插入 profile
插件契约
安装 dock-base 时,本插件遵循 dock-base 插件契约,通过 ctx.workbench 方法调用协作:
| 注册项 | API | 说明 |
|---|---|---|
| 侧边栏面板 | ctx.workbench.registerPanel() |
快捷控制面板(sideBar 区域) |
| 活动栏图标 | ctx.workbench.registerActivityBarItem() |
闪电图标⚡,点击打开侧边栏 |
| 编辑器视图 | ctx.workbench.registerEditorView() |
快捷控制面板(可拖出为浮动窗口) |
| 命令 | ctx.workbench.registerCommand() |
dock-flash:openQuickControl 命令 |
| 快捷开关注册表 | ctx.provide('quickControl', registry) |
供其他插件注册开关 |
未安装 dock-base 时,dock-flash 自动进入独立模式:直接在 DOM 中注入悬浮⚡触发按钮和弹出面板,提供所有非布局类开关。
🧩 动态发现 API
本插件发布 quickControl 服务到 WorkbenchContext。其他插件通过 ctx.get('quickControl') 获取注册表并注册自己的快捷开关。
开关类型
| 类型 | 说明 | 必需字段 |
|---|---|---|
toggle |
布尔开关 | getValue(), setValue(boolean) |
slider |
数值滑块 | getValue(), setValue(number), min, max, step |
select |
下拉选择 | getValue(), setValue(any), options |
buttongroup |
按钮组 | getValue(), setValue(any), options |
action |
操作按钮 | run() |
QuickSwitchDefinition
interface QuickSwitchOption {
label: string | (() => string)
value: any
}
interface QuickSwitchDefinition {
/** 全局唯一 id,建议用 "插件名:开关名" 格式,如 "dock-git:show-stash" */
id: string
/** 显示标签(支持函数式 i18n) */
label: string | (() => string)
/** 图标(emoji 或文字) */
icon?: string
/** 开关类型 */
type: 'toggle' | 'slider' | 'select' | 'buttongroup' | 'action'
/** 排序权重(升序),内置项用 10-60,建议从 100 开始 */
order?: number
/** 内置分组:'appearance' | 'layout' | 'system'(仅 dock-flash:* 内置项有效,第三方开关忽略此字段) */
group?: string
/**
* 可选的簇标签。共享同一标签的开关会被画成一张卡片,并作为一个单位排序
* (共用一组 ▲▼),簇内顺序固定为各自的 `order` 值。卡片默认只显示首行、
* 处于折叠状态,成员可由用户用卡片底部居中的 ▼/▲ 展开 —— 簇不会自行消失,因此面板结构与已保存的
* 排序不会因为条件变化而改变。
*/
cluster?: string
// ── toggle / slider / select / buttongroup 通用 ──
getValue?: () => any
setValue?: (value: any) => void
// ── slider 专用 ──
min?: number
max?: number
step?: number
formatLabel?: (value: number) => string
// ── select / buttongroup 专用 ──
options?: QuickSwitchOption[] | (() => QuickSwitchOption[])
// ── action 专用 ──
run?: () => void | Promise<void>
actionLabel?: string
/** 去掉标题列、按钮占满整行(按钮自带文案时用) */
hideLabel?: boolean
}
注册示例
// 在其他插件的 client.js factory 中:
exports.inject = ['quickControl'] // ← 声明服务依赖
exports.apply = function (ctx) {
const registry = ctx.get('quickControl')
ctx.effect(() => {
const dispose = registry.registerSwitch({
id: 'dock-git:show-stash',
label: 'Show Stash',
icon: '📦',
type: 'toggle',
order: 100,
getValue: () => myGitState.showStash,
setValue: (v) => { myGitState.showStash = v }, // 只更新状态,面板自动处理 UI
})
return dispose // 卸载时自动反注册
}, 'dock-git: quick-control switch')
}
服务依赖声明
第三方插件必须声明对 quickControl 服务的依赖,确保 dock-flash 先完成注册再调用 apply()。在 client half 中添加 exports.inject:
// client.js — factory 内
exports.inject = ['quickControl'] // ← 必须声明,否则服务可能尚未就绪
exports.apply = function (ctx) {
const registry = ctx.get('quickControl') // 服务已就绪,无需 null 检查
// …
}
同时在 package.json 的 dsh.client.inject 中声明模块级依赖,确保 dock-flash 的 client 脚本先于本插件加载:
{
"dsh": {
"client": {
"inject": ["@deepseek-ai/dsh-client-runtime", "dock-flash/client"]
}
}
}
动态值更新
如果开关的值在面板外部发生变化(如定时器、服务器推送、其他 UI 操作),调用 registry.notifyChange(id) 触发面板刷新:
const registry = ctx.get('quickControl')
// 某个异步事件导致值变了
myGitState.showStash = true
registry?.notifyChange('dock-git:show-stash')
注意:用户在面板中操作开关时,dock-flash 会自动刷新该开关的 UI,无需手动调用
notifyChange。此方法仅在面板感知不到的外部值变化时使用。
变更日志
面板自动为每次用户交互(点击 toggle、拖动 slider、选择 option、点击 buttongroup / action 按钮)记录变更日志,在"最近修改"选项卡中展示(30 秒自动过期)。
插件不需要手动调用 _notifyChange。面板在渲染每个开关时已注入 _notifyChange 方法并在用户交互时自动调用。setValue() 只需更新内部状态:
// ✅ 正确:setValue 只更新状态
setValue: (v) => { myState = v }
// ❌ 错误:不要在 setValue 中调用 _notifyChange(会产生重复记录)
setValue: (v) => { myState = v; sw._notifyChange?.('旧', '新') }
_notifyChange(oldDisplay, newDisplay) 仅在插件主动改变状态(非用户面板操作)且需要记录变更日志时使用,例如:
// 定时器自动切换深色模式
setTimeout(() => {
state.darkMode = true
const sw = registry.getSwitches().find(s => s.id === 'my-plugin:auto-dark')
sw?._notifyChange('浅色', '深色')
}, 3600000)
分组规则
面板采用可折叠选项卡布局:
- ⚡ 工作台 — 内置开关(id 以
dock-flash:开头),按group字段分为外观 / 布局 / 系统三个子组。集成模式下「布局」子组为空、不渲染 —— 停靠布局属性全部由 dock-base 自己的设置负责。 - 🧩 扩展 — 第三方开关(id 不以
dock-flash:开头),按 id 冒号前缀(即插件名)自动分子组 - 📝 最近修改 — 最近 30 秒内的开关变更记录
判定规则:id.startsWith('dock-flash:') 为内置,否则为第三方。第三方开关的 group 字段在当前版本中被忽略,统一归入扩展标签页按来源插件分组。
同组内按 order 升序排列。
quickControl 服务 API
| 方法 | 说明 |
|---|---|
registerSwitch(def) |
注册开关,返回 dispose 函数 |
unregisterSwitch(id) |
按 id 反注册 |
getSwitches() |
获取所有开关(按 order 升序) |
notifyChange(id) |
通知开关值已变化,触发面板刷新 |
recordChange(entry) |
记录一条变更日志 { id, label, icon, oldDisplay, newDisplay } |
getChangelog() |
获取最近 30 秒内的变更记录 |
subscribe(fn) |
订阅注册表/值变化,返回 dispose 函数 |
version |
当前注册表版本号(每次变更递增) |
🎨 皮肤系统
皮肤切换器需要 dsh-market 插件才能使用 — 没有市场插件时不显示(禁用态皮肤对 DOM 扫描不可见,列表会不完整)。
皮肤分类
| 分类 | 说明 | 示例 |
|---|---|---|
| CSS 皮肤 | 通过 <style> / <link> 标签 + body 属性激活 |
maid-atelier、official-homepage |
| 托管皮肤 | 自带 canvas/WebGL/粒子等生命周期,通过 mount() / unmount() 切换 |
Mineradio |
| 排除项 | 匹配发现规则但从下拉菜单中隐藏(冲突或外部控制不可靠) | bloom-theme、black-hole、theme-manager |
dsh-market 集成
- 获取已安装/禁用态的完整主题列表(
/dsh-market/installedAPI) - 市场管理的主题通过
/dsh-market/use-skinAPI 激活(会触发页面刷新) - 市场禁用的主题在下拉菜单中标注「未启用」
偏好持久化
皮肤选择、面板排序与独立模式的触发位置都保存在宿主设置命名空间(配置目录 settings.yaml 里的 dock-flash),加载后自动恢复。localStorage 只作为缓存,因此这些偏好会跟着你换浏览器、换机器,而不是留在浏览器里。升级前只存在于 localStorage 的旧值,会在首次加载时一次性迁移进配置。
切换机制、排除原因、技术约束等实现细节见 AGENTS.md。
🌐 国际化
面板内置中英文本地化,自动跟随 DSH 语言设置。第三方开关可使用函数式标签(label: () => t('xxx'))实现语言切换时动态刷新。
安装
需要 DSH Web 环境:
# 安装本插件
dsh plugin --profile my-profile add ./dock-flash
# (可选)安装 dock-base 以获得完整的工作台集成
dsh plugin --profile my-profile add dock-base
# 启动
dsh --profile my-profile
无 dock-base:dock-flash 以独立模式运行 — ⚡ 触发按钮注入到 trigger-position 开关所选中的会话槽位(默认:输入框右侧),点击展开快捷控制浮动面板(外观、布局(触发位置)、系统开关)。共享槽位 sidebar.footer.action 有意不提供,因为其他插件也占用它。
有 dock-base:dock-flash 集成到工作台 — ⚡图标出现在活动栏,面板可作为侧边栏或浮动窗口打开,包含外观与系统开关。停靠布局属性(停靠边、自动隐藏、保留空间、图标缩放)在 dock-base 自己的设置里配置,不在这里。
开发
pnpm install
pnpm run build # tsc → dist/index.js
pnpm run typecheck # 仅类型检查
开发规则、关键约束与测试规范见 AGENTS.md;各版本的改动内容与原因见 CHANGELOG.md。
样式契约
本插件遵循 DSH Web 样式契约:全部颜色经 --dsw-alias-* 设计令牌引用(字面量仅作兜底),无按主题分支的 CSS 选择器。
依赖
| 依赖 | 类型 | 说明 |
|---|---|---|
dock-base >=0.1.2-0 <1.0.0-0 || >=0.2.0-0 <1.0.0-0 |
peer(可选) | 提供 ctx.workbench 注册表服务,用于完整工作台集成 |
@deepseek-ai/cordis >=4.0.0-rc.1 <5.0.0-0 || >=4.0.1-0 <5.0.0-0 |
peer | 插件框架(DSH 自带) |
许可证
链接
同类插件
zhu1090093659/dsh-web#packages/dsh-task-board★ 7809
侧边栏多列任务看板:卡片交给真实 DSH 智能体会话执行,支持 cron 定时(Host 侧到点执行,关浏览器也生效)。
zhu1090093659/dsh-web#packages/dsh-web-all★ 7809
DSH Web UI 插件与皮肤合集:任务看板、git 图、右侧面板、远程移动端 UI、桌宠、实时 token 统计与皮肤中心。
omdsh-dev/DSH-better-sidebar★ 3680
侧边栏完整工作台:内置文件渲染编辑、终端、Git 与子代理,支持三方插件注册新 Tab。
ccch1mneyyy/dsh-TUI★ 3110
Claude Code 风格全屏终端 UI:像素鲸鱼顶栏、实时工作状态行、思考流式展开。
MeteorNOX/DeepSeek-Balance-Whale-Widget★ 2750
右下角常驻的小鲸鱼挂件:余额、今日已用与每轮对话消耗(含峰谷价),余额预警与今日预算的泡泡内容都可编辑;泡泡点击序列模块化自定义,支持并列加权 A/B、随机台词与随机图片;内置 30+ 厂商模板(OpenAI / OpenRouter / Kimi / 硅基流动 / 方舟 / 智谱 / MiniMax 等),按模型查余额与订阅额度;另有任务结束音效、导入音频、自定义角色与资源管理。数据全在本机,无遥测。
Devin-AXIS/deepseek-design#deepseek-idesign★ 1359
可视化设计工作室,支持网站、App 原型、海报、信息卡、报告和杂志的模板创建、元素编辑、选区级 AI 草稿衔接与导出。
社区评论
评论公开保存在 GitHub Discussions。加载评论会连接 GitHub 和 Giscus;发表内容需要 GitHub 账号。