@dickpy/dsh-weblink
给 DeepSeek Harness 桌面端加一个网站面板:
左侧栏一个图标按钮,点开后把中间主区域整块变成一个真正的、多标签的浏览器
(Electron <webview>,不是 iframe),起始网址可配置;点任意会话(含「新对话」)主区域自动切回
对话界面,而浏览器在后台继续活着——标签、滚动位置、登录态都在,再点回来接着用。
安装
在 DSH 桌面端里:侧栏打开 插件 → 添加插件 → 填本仓库地址 → 安装 → 重启桌面端。
安装器会把包登记进 profile 的 dsh.profile.bundles,包自带的 cordis.patch.yml 随即插入插件行,
所以不需要你手写任何配置。
为什么要重启:@deepseek-ai/dsh-hmr 在 profile 宿主里默认只监听配置文件、不监听插件源码模块,
宿主半边的新模块必须重启才会加载。安装后第一次使用请重启一次;之后只有插件自身改代码才需要再重启。
要求:DSH 桌面端(Windows / macOS / Linux 均可)。纯浏览器里跑 dsh web 时没有桌面浏览器桥,
面板会显示一行说明,不会降级成 iframe。
使用
-
点左侧栏的图标(默认名字「浏览器」)→ 主区域变成浏览器,并打开配置里的起始网址。
-
标签:顶部标签条可以切换、关闭;+ 新开一个(用配置里的起始网址)。
页面里点开会新窗口的链接(window.open / target=_blank,含 Ctrl+点击)会自动开一个新标签,
和普通浏览器一样。
-
工具栏:后退 / 前进 / 刷新 / 地址栏(回车前往)/ 在系统浏览器中打开 / 返回对话。
-
切走不丢:点任意会话回到对话,浏览器只是被隐藏;再点左侧栏图标,标签、页面状态、登录态都还在。
-
设置 → 网站面板:
| 字段 | 含义 |
|---|
| 起始网址 | 新标签打开的地址;不写协议按 https:// 补全,只接受 HTTP(S) |
| 左侧栏名称 | 图标旁的文字(侧栏收起时作为提示与无障碍名称) |
| 排序 | 数字越小越靠上 |
字段带「已覆盖 / 恢复默认」,只有点保存才写入;写错地址或非数字会被 schema 拒绝,不会静默存进去。
保存后左侧栏那一行的文字与位置立即跟着变。已经打开的标签不会被改动——改起始网址只影响之后新开的标签。
它怎么工作
席位
| 部件 | 席位 | 说明 |
|---|
| 左侧图标按钮 | sidebar.panellist(list,root) | 由 ui-sidebar 渲染,点击回调 ctx.layout.selectPanel(id) |
| 主区域占位 | main(keyed,root,key = weblink) | 只负责测量:它报出自己占的矩形,浏览器本身不在这里 |
| 真正的浏览器 | shell.overlay(list,root) | 常驻、永不卸载;position: fixed 定位到上面那个矩形 |
| 设置页 | settings.section(list,root) | 官方 SettingsFormModel + SettingsForm |
| 切回对话 | 打开会话时 ctx.layout.selectPanel(null) | 产品自带行为,本插件不参与 |
为什么要拆成两半:main 是 keyed 席位,换 key 时旧的条目会被卸载(renderer 只渲染匹配 key 的
那一个条目)。而 Electron 的 guest 在元素离开文档时就被销毁——所以只要浏览器还住在 main 里,
点一下会话就会把所有标签连同页面状态一起杀掉。把浏览器放进常驻的 shell.overlay、main 里只留
一个测量占位,切走时仅仅是 display: none,标签全程活着。
容器用 z-index: 10:盖得住主区域内容,又低于菜单/弹窗层(应用自己的浮层是 1100)。
配置
配置的真源是宿主半边的 Config(schemastery),三个字段都是 .volatile()——volatile 意味着保存后由
Loader 就地提交、不重挂插件。浏览器半边用 ctx.configForms.get('weblink') 读同一个值,持久化由宿主
自己写回当前 profile 的 Cordis patch,不需要使用者打开那个文件。
浏览器本体
每个标签各调一次 window.dshDesktop.browser.acquire() 拿到独立租约,各挂一个
<webview src="about:blank#<lease>" partition="...">;主进程校验租约后才放行,并套上固定隔离策略
(sandbox、无 Node、无权限、禁下载、弹窗转交插件)。所有标签传同一个 workspace key,
因此共用同一个存储分区——和普通浏览器的多标签一样共享登录态。
页面里的新窗口请求由主进程 setWindowOpenHandler 拦下并回推给插件,插件据此开新标签。
边界
- 只在桌面端可用:纯浏览器里
window.dshDesktop 没有 browser 桥,面板显示说明文字。
- 表单 POST 打开的新窗口会被丢弃:主进程只在
postBody 为空时才把新窗口请求转给插件。
普通链接、window.open、target=_blank、Ctrl+点击都正常。
- 隐藏期间标签仍在运行:后台标签里的定时器、音视频不会被暂停(和浏览器后台标签一致),
所以别让它偷偷播声音。
- 登录态:同一次运行内跨标签、跨开关共享;退出应用即清空——分区名每次启动随机生成,
且是不带
persist: 前缀的内存 session,不落盘。与内置右栏浏览器 tab、与系统浏览器都互不共享。
- 右侧栏(dock)里的内置浏览器 tab 与本插件互不影响,可以同时用。
开发
lib/index.js 宿主半边:Config schema(volatile)+ 只读诊断路由
lib/client.js 浏览器半边:侧栏行、main 占位、shell.overlay 里的多标签浏览器、设置页
cordis.patch.yml 作为 bundle 安装时插入插件行的模板
locale/{zh,en}.json 插件管理器卡片上的标题与说明
check-host.mjs 宿主半边离线自检(npm run check)
toggle-weblink.ps1 手工安装时的开关脚本
自检:
npm install # 或 pnpm install,装上 @deepseek-ai/schemastery
node check-host.mjs
exports: Config, apply, inject, name
defaults: {"url":"https://www.bing.com","label":"浏览器","order":40}
explicit: {"url":"https://example.com","label":"示例","order":5}
invalid order: rejected -> $.order expected number but not-a-number
三个容易踩的坑(都踩过了):
- 改了
lib/index.js 必须重启桌面端:HMR 默认 root: [],只监听配置不监听源码模块;
实测改文件、甚至把插件行删掉再加回来都不会重新 import(Node ESM 缓存)。
只改 lib/client.js 则刷新窗口即可,宿主每次从磁盘重取浏览器半边。
- 用
link: / junction 方式开发时,插件目录必须自带 node_modules:
junction 会被 Node 按 realpath 解析,裸包名 @deepseek-ai/schemastery 不会回落到 profile 的
node_modules,缺了它宿主半边直接 ERR_MODULE_NOT_FOUND。
正常的 pnpm add <git-url> 安装不会有这个问题。
- 绝不要移动或卸载
<webview> 元素:guest 在元素离开文档时销毁。本插件把所有标签放在同一个
常驻容器里,只切 display;容器本身也从不换父节点。想改这块之前先读 lib/client.js 顶部的注释。
GET /api/dsh-weblink/settings 是宿主侧的只读诊断镜像,返回当前生效配置;可以用它确认宿主模块
是否已更新(返回里带一个 build 标记)。
手工安装 / 临时禁用
不想走插件管理器时(例如本机开发):
# 每个 profile:package.json 加 "@dickpy/dsh-weblink": "link:<插件目录>"
# node_modules/@dickpy/dsh-weblink 建同名 junction
# cordis.patch.yml 追加:
# - insert:
# - id: weblink
# name: "@dickpy/dsh-weblink"
桌面端固定使用 ~/.dsh/profiles/desktop(resolveDesktopPaths() 写死,跟
profile-selection/state.json 里的 active 无关);dsh web 用 ~/.dsh/profiles/web。
开关脚本只编辑 profile patch 里的行(先备份),配置值从不经它写:
powershell -ExecutionPolicy Bypass -File toggle-weblink.ps1 -State status
powershell -ExecutionPolicy Bypass -File toggle-weblink.ps1 -State off
Windows 默认禁止运行 .ps1;脚本保持纯 ASCII,因为 Windows PowerShell 会把无 BOM 的脚本按 ANSI 读。
已验证
在 Windows + DSH Desktop 0.2.0-rc 上实测:插件行加载后左侧栏出现按钮;点击后主区域渲染出目标站点
(无障碍树里能读到页面内容,确认是真 webview 而非空 iframe);在设置页保存网址与名称后,
宿主把它写进了 profile 的 Cordis patch,诊断镜像立即返回新值。
多标签与后台保活基于对 main.js(DesktopBrowserGuests)与 ui-layout/renderer 的源码核对实现;
<webview> 元素换父节点会销毁 guest 这一点按 Electron 语义规避(容器从不换父节点,只切 display)。
License
MIT