dsh-sidebar-browser-agent 0.2.8 → 0.2.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +15 -217
- package/lib/client.js +130 -18
- package/lib/{entry-0.2.8.js → entry-0.2.10.js} +1 -1
- package/lib/index.js +1 -1
- package/package.json +2 -2
- package/test/client-harness.mjs +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.10
|
|
4
|
+
|
|
5
|
+
- README is now just what the plugin does and its limits, with a hero diagram
|
|
6
|
+
of the tool → native webview flow.
|
|
7
|
+
- The plugin page shows the same diagram as an official-style hero card above
|
|
8
|
+
the settings form (localized heading/subtitle).
|
|
9
|
+
|
|
10
|
+
## 0.2.9
|
|
11
|
+
|
|
12
|
+
- Polished the official-style plugin page rhythm: field/row spacing, tertiary
|
|
13
|
+
12px hints, the same input surface and radius the built-in settings pages
|
|
14
|
+
use, and the shared form footer as the only save action.
|
|
15
|
+
|
|
3
16
|
## 0.2.8
|
|
4
17
|
|
|
5
18
|
- The plugin page now follows the official plugin-page style: the configuration
|
package/README.md
CHANGED
|
@@ -1,227 +1,25 @@
|
|
|
1
1
|
# 侧栏浏览器 Agent 控制(dsh-sidebar-browser-agent)
|
|
2
2
|
|
|
3
|
-
给
|
|
4
|
-
点击、输入、按键、滚动、执行 JS、截图返回给模型查看;带配置开关、启动自检与回归测试。
|
|
3
|
+
给 DeepSeek Harness 右侧栏的**原生浏览器**加上 Agent 可操作能力。
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
侧栏浏览器自带的 Electron `<webview>` 客户端的 Chromium API:
|
|
5
|
+

|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
- `sendInputEvent()` —— 原生鼠标/键盘事件(真实点击、按键);
|
|
11
|
-
- `capturePage()` —— Chromium 原生截图,经 attachment 服务变成图像内容块返回给模型;
|
|
12
|
-
- `loadURL()` / `reload()` —— 原生导航与刷新,侧栏地址栏同步更新。
|
|
7
|
+
## 做什么
|
|
13
8
|
|
|
14
|
-
|
|
9
|
+
- 管理右侧栏浏览器标签页:`sidebar_browser`(status / list / open / navigate / reload / focus / close)。
|
|
10
|
+
- 操作真实页面:`sidebar_page`(read 文本 / 交互元素 / HTML、click、type、press、scroll、wait、eval、screenshot)。
|
|
11
|
+
- 全原生:直接调用 Electron `<webview>` 的 `executeJavaScript`、`sendInputEvent`、`capturePage`、`loadURL` —— 不使用 Playwright/Puppeteer、不注入代理、不重写页面。
|
|
12
|
+
- 截图以图片附件返回给模型;配置页可开关工具、允许 eval / 原生输入、设置域名白名单。
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
| --- | --- |
|
|
18
|
-
| `cordis.patch.yml` | bundle patch,插入 Host 插件行(id `sidebar-browser-agent`) |
|
|
19
|
-
| `lib/index.js` | Host 半源码:配置与开关、`sidebar_browser` / `sidebar_page` 工具、HTTP 长轮询桥、截图转 attachment、启动自检 |
|
|
20
|
-
| `lib/entry-<version>.js` | 实际提供给 Loader 的 Host 入口(由 `scripts/set-version.mjs` 生成):每次发版换新文件路径,重装后能加载新 Host 代码而非 Node 缓存的旧模块 |
|
|
21
|
-
| `lib/client.js` | 浏览器半:长轮询取命令,用公开 `ctx.sidebarRight` 管理标签、用原生 webview 操作页面;所有 guest 调用有超时看门狗,页面卡死不会阻塞桥;`status` 输出自检信息 |
|
|
22
|
-
| `locale/*.json` | 插件列表显示用的标题与描述(中/英) |
|
|
23
|
-
| `icon.svg` | 插件图标 |
|
|
24
|
-
| `vendor/` | 内置的 MIT 依赖(schemastery + cosmokit),使 link 安装的插件也能声明原生 Config schema,见 `vendor/NOTICE.md` |
|
|
25
|
-
| `test/` | 零依赖离线回归测试(Host 桥/工具/配置 + Client guest 脚本),`node test/run.mjs` |
|
|
26
|
-
|
|
27
|
-
### 为什么需要桥
|
|
28
|
-
|
|
29
|
-
右侧栏布局与标签状态属于浏览器页面,只有页面持有 `ctx.sidebarRight`。Host 工具把命令排入队列,
|
|
30
|
-
页面半长轮询取走命令、执行后回传结果(截图走同一条通道)。所有桥路由都经过
|
|
31
|
-
`ctx.connection.requestRejection` 的准入检查。
|
|
32
|
-
|
|
33
|
-
### webview 定位方式
|
|
34
|
-
|
|
35
|
-
侧栏为每个标签渲染 title 和 body 两个带 `data-sidebar-right-tab="<tabId>"` 的节点,
|
|
36
|
-
外层是 `[data-sidebar-right-session]`。body 节点内是原生 `<webview data-sidebar-browser-frame>`,
|
|
37
|
-
插件按 (sessionId, tabId) 精确定位它,不依赖标签顺序或坐标猜测;找不到时返回明确诊断而不是静默失败。
|
|
38
|
-
|
|
39
|
-
## 工具
|
|
40
|
-
|
|
41
|
-
### `sidebar_browser` —— 标签页管理
|
|
42
|
-
|
|
43
|
-
| action | 说明 | 参数 |
|
|
44
|
-
| --- | --- | --- |
|
|
45
|
-
| `status` | 桥状态、载体类型、原生能力、插件版本、配置、attachment 自检 | — |
|
|
46
|
-
| `list` | 列出标签页(含原生读取的 URL / 标题) | — |
|
|
47
|
-
| `open` | 新建标签页打开网页 | `url` |
|
|
48
|
-
| `navigate` | 在当前标签页内原生导航 | `tabId`、`url` |
|
|
49
|
-
| `reload` | 原生刷新 | `tabId` |
|
|
50
|
-
| `focus` | 切换到标签页 | `tabId` |
|
|
51
|
-
| `close` | 关闭标签页 | `tabId` |
|
|
52
|
-
|
|
53
|
-
### `sidebar_page` —— 页面读取与操作
|
|
54
|
-
|
|
55
|
-
| action | 说明 | 参数 |
|
|
56
|
-
| --- | --- | --- |
|
|
57
|
-
| `read` | 读取页面:`text` 可见文本 / `interactive` 可交互元素(含 CSS selector 与坐标)/ `html` 源码 | `mode`、`selector`、`maxChars`、`maxElements` |
|
|
58
|
-
| `click` | 原生点击:按 CSS selector、按可见文本、或按视口坐标 | `selector` / `contains` / `x`+`y` |
|
|
59
|
-
| `type` | 聚焦输入框写入文本(可替换原值、可回车提交),触发真实 input/change 事件 | `selector`、`text`、`clear`、`submit` |
|
|
60
|
-
| `press` | 原生按键:Enter/Tab/Escape/Backspace/Delete/方向键/PageUp/PageDown/Home/End/Space | `key` |
|
|
61
|
-
| `scroll` | 滚动页面或把元素滚入视口 | `dy`、`dx`、`selector` |
|
|
62
|
-
| `wait` | 轮询等待 selector 或页面文本出现 | `selector` / `contains`、`timeoutMs` |
|
|
63
|
-
| `eval` | 在页面执行 JavaScript,返回 JSON 结果(可做任意 DOM 操作) | `code` |
|
|
64
|
-
| `screenshot` | 截取当前视口,图像作为内容块返回给模型 | — |
|
|
65
|
-
|
|
66
|
-
`read` 的 `interactive` 输出示例:
|
|
67
|
-
|
|
68
|
-
```
|
|
69
|
-
[2] <button type=button> "Say hello" selector=#greet @(118,261)
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
随后即可 `click --selector "#greet"`。页面结构未知时先 `read interactive`。
|
|
73
|
-
|
|
74
|
-
## 配置
|
|
75
|
-
|
|
76
|
-
插件把四个字段声明为**原生 Schemastery `Config`(全部 `.volatile()`)**,并随包提供配置页组件,因此在
|
|
77
|
-
**Plugin Manager → 对应 bundle → 该行 → Configure** 可直接编辑:
|
|
78
|
-
|
|
79
|
-
| 字段 | 默认 | 说明 |
|
|
80
|
-
| --- | --- | --- |
|
|
81
|
-
| `enabled` | `true` | 是否对 Agent 暴露 `sidebar_browser` / `sidebar_page`(关闭后工具从模型视图隐藏,桥仍在,便于诊断) |
|
|
82
|
-
| `allowedHosts` | `[]` | 允许 `open`/`navigate` 的域名白名单;空 = 全部 HTTP(S)。每行一个,`example.com` 同时允许子域,`localhost:8765` 精确匹配主机与端口 |
|
|
83
|
-
| `allowEval` | `true` | 是否允许 `sidebar_page` 的 `eval`(页面内任意 JS) |
|
|
84
|
-
| `allowNativeInput` | `true` | 是否允许 `click` / `type` / `press` 原生输入 |
|
|
85
|
-
|
|
86
|
-
因为字段是 volatile 的,保存后**立即生效、无需重启**:工具显隐由每步的 `when` 条件读取实时值,
|
|
87
|
-
各开关也在每次调用时实时读取。页面上的配置来自 profile patch 的持久化写入(与手写 patch 等价):
|
|
88
|
-
|
|
89
|
-
```yaml
|
|
90
|
-
- id: sidebar-browser-agent
|
|
91
|
-
config:
|
|
92
|
-
enabled: true
|
|
93
|
-
allowedHosts: []
|
|
94
|
-
allowEval: true
|
|
95
|
-
allowNativeInput: true
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
若部署中没有 settings 服务,配置页会显示「不可编辑」,插件本身仍按 patch 中的值运行。
|
|
99
|
-
|
|
100
|
-
## 诊断与自检
|
|
101
|
-
|
|
102
|
-
- 插件启动时 `apply` 会做一次自检:`webServer` / `connection` / `tools` / `attachments` 是否就绪,
|
|
103
|
-
通过日志输出 `sidebar-browser-agent 0.2.0: ready (...)`;attachment 缺失会 warn(截图不可用)。
|
|
104
|
-
- `sidebar_browser status` 的 note 末尾带:
|
|
105
|
-
- `checks={...}`:页面半自检,含插件版本、载体、DOM 标记、guest webview、原生 API 清单、
|
|
106
|
-
当前 guest URL、Harness release(版本 + commit);
|
|
107
|
-
- `host-plugin=...`:Host 版本、生效配置、attachment 可用性。
|
|
108
|
-
- 原生 API 或 DOM 标记缺失时,`checks.apiMissing` / `surface=false` / `webview=false` 会直接暴露问题。
|
|
109
|
-
|
|
110
|
-
## 安装
|
|
111
|
-
|
|
112
|
-
### 从 GitHub 安装(推荐给使用者)
|
|
113
|
-
|
|
114
|
-
Plugin Manager(或 `plugin_manager` 工具)接受 pnpm 能解析的安装规格。仓库带标签后:
|
|
115
|
-
|
|
116
|
-
```text
|
|
117
|
-
github:Hesitate-P/dsh-sidebar-browser-agent#v0.2.2
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
- Web 界面:Plugins → 添加插件 → 粘贴上面的规格(DSH 会先用 `git ls-remote` 检查 GitHub 连通性,再交给 pnpm 安装);
|
|
121
|
-
- Agent 工具:`plugin_manager install_bundle(target: "github:Hesitate-P/dsh-sidebar-browser-agent#v0.2.2")`。
|
|
122
|
-
|
|
123
|
-
也可以直接填 GitHub 仓库 URL。安装完成后 profile 依赖会记录为 `github:...#v0.2.0`,bundle 自动选中并启用。
|
|
124
|
-
`engines.dsh` 声明兼容 `>=0.2.2-rc.1 <0.3.0`,Git 规格在安装后由 DSH 校验版本要求。
|
|
125
|
-
|
|
126
|
-
### 从 npm 安装(推荐,可一键更新)
|
|
127
|
-
|
|
128
|
-
包名 `dsh-sidebar-browser-agent`(已发布,最新 `0.2.5`)。在 Plugin Manager 中以
|
|
129
|
-
`dsh-sidebar-browser-agent@<version>` 安装(或直接点 Update 升级),此后详情页会持续提供 **Update**
|
|
130
|
-
(DSH 的版本列表与 Update 只对 registry 安装的第三方包生效):
|
|
131
|
-
|
|
132
|
-
```text
|
|
133
|
-
plugin_manager install_bundle(target: "dsh-sidebar-browser-agent@0.2.5")
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
### 开发安装(link)
|
|
137
|
-
|
|
138
|
-
通过 Plugin Manager 安装本目录的**绝对路径**:
|
|
139
|
-
|
|
140
|
-
```text
|
|
141
|
-
plugin_manager install_bundle(target: /path/to/sidebar-browser-agent)
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
profile 以 `link:` 指向该目录,**不要移动或删除它**。Host 半代码修改后重启 Harness
|
|
145
|
-
(或对该行 disable→enable)才会重新加载;`lib/client.js` 修改会被客户端 HMR 自动热换。link 安装没有 Update 按钮。
|
|
146
|
-
|
|
147
|
-
## 更新
|
|
148
|
-
|
|
149
|
-
DSH Plugin Manager 的 **Update** 按钮只接受"以自身包名保存的 registry spec"的第三方包;
|
|
150
|
-
GitHub 安装(`github:...` 规格)不会出现 Update。对应更新方式:
|
|
151
|
-
|
|
152
|
-
| 安装来源 | 更新方式 |
|
|
153
|
-
| --- | --- |
|
|
154
|
-
| GitHub 安装 | Plugin Manager 中先 **Remove**,再安装新标签:`github:Hesitate-P/dsh-sidebar-browser-agent#v0.2.2`。工具等价操作:`plugin_manager remove_bundle` → `install_bundle`。移除期间插件短暂不可用,重新安装后恢复。 |
|
|
155
|
-
| npm 安装 | 详情页 **Update**:列出更高版本、校验 `engines.dsh`/peer 后一键替换;失败会回滚并重装旧版本。 |
|
|
156
|
-
|
|
157
|
-
更新后先跑一次 `sidebar_browser status` 确认版本:自 0.2.5 起 Host 入口使用版本化文件路径,
|
|
158
|
-
Plugin Manager 重装新版本后解析到新的模块 URL,Host 半会加载新代码而**无需重启** Harness;
|
|
159
|
-
若仍看到「page half / Host half 版本不一致」告警,重启一次 Harness 即可。
|
|
160
|
-
|
|
161
|
-
如果希望 GitHub 安装也具备一键更新:把 profile 的安装来源切换到 registry spec
|
|
162
|
-
(`dsh-sidebar-browser-agent@<version>`)一次即可;本仓库自 `0.2.5` 起已发布到 npm。
|
|
163
|
-
|
|
164
|
-
## 发布流程
|
|
165
|
-
|
|
166
|
-
发布认证使用 **npm Trusted Publishing(GitHub OIDC)**,不使用长期 token:npm 包设置里把发布者绑定到
|
|
167
|
-
本仓库的 `publish.yml`,workflow 以 `id-token: write` 换取短期发布权限(`npm publish --provenance`)。
|
|
168
|
-
这样没有需要轮换的 `NPM_TOKEN`,也不受 bypass-2FA token 弃用政策影响。
|
|
169
|
-
|
|
170
|
-
首次发布前在 npm 包设置(Settings → Trusted Publisher)绑定:owner `Hesitate-P`、repository
|
|
171
|
-
`dsh-sidebar-browser-agent`、workflow `publish.yml`。
|
|
172
|
-
|
|
173
|
-
```bash
|
|
174
|
-
node scripts/set-version.mjs 0.2.1 # 同步 package.json、index.js/client.js 的 PLUGIN_VERSION,并生成新的 lib/entry-<version>.js
|
|
175
|
-
npm test # 回归测试同时校验版本一致性、版本化 Host 入口
|
|
176
|
-
git commit -am "release v0.2.1"
|
|
177
|
-
git tag v0.2.1
|
|
178
|
-
git push --follow-tags # CI 跑测试;publish workflow 通过 OIDC 自动发布 npm
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
### 本地打包
|
|
182
|
-
|
|
183
|
-
```bash
|
|
184
|
-
npm run pack:check # 校验 npm pack 文件清单
|
|
185
|
-
npm pack # 生成 dsh-sidebar-browser-agent-0.2.0.tgz
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
`files` 已显式包含运行文件、`vendor/`、`test/`、`README.md`、`CHANGELOG.md`、`LICENSE`。
|
|
189
|
-
|
|
190
|
-
变更记录见 [CHANGELOG.md](CHANGELOG.md)。
|
|
191
|
-
|
|
192
|
-
## 测试
|
|
193
|
-
|
|
194
|
-
```bash
|
|
195
|
-
node test/run.mjs # 或 npm test
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
- `test/host-harness.mjs`:真实 Config 校验、开关门禁、工具注册、输出 schema、HTTP 桥往返、
|
|
199
|
-
截图 attachment + 图像块、版本一致性;
|
|
200
|
-
- `test/client-harness.mjs`:在 vm + 假 DOM 中执行真实 guest 脚本,覆盖 read/click/type/press/
|
|
201
|
-
scroll/wait/eval/screenshot、原生导航刷新、后台会话与自检输出。
|
|
202
|
-
|
|
203
|
-
零第三方依赖、无需运行中的 Harness。
|
|
204
|
-
|
|
205
|
-
## 跨会话(后台)行为
|
|
206
|
-
|
|
207
|
-
页面切到别的会话时(`status` 的 `mounted` 是另一个会话 id):
|
|
208
|
-
|
|
209
|
-
- **可用**:`read` / `click` / `type` / `press` / `scroll` / `wait` / `eval`、`navigate` / `reload`、
|
|
210
|
-
`list` / `status`。原生 webview 在会话切走后仍保持挂载,结果会附带「操作未显示在屏幕上」的说明。
|
|
211
|
-
- **不可用**:`open` / `focus` / `close`(改变可见侧栏布局,必须让调用会话在屏幕上);
|
|
212
|
-
`screenshot`(Chromium 无法截取隐藏的 guest,会返回明确提示,切回即可)。
|
|
14
|
+
安装:`dsh plugin --profile <profile> add dsh-sidebar-browser-agent`(要求 DSH `>=0.2.2-rc.1 <0.3.0`)。
|
|
213
15
|
|
|
214
16
|
## 限制
|
|
215
17
|
|
|
216
|
-
-
|
|
217
|
-
|
|
218
|
-
-
|
|
219
|
-
-
|
|
220
|
-
-
|
|
221
|
-
-
|
|
222
|
-
- 侧栏浏览器对页面有沙箱限制:无下载、无弹窗(会转为新侧栏标签)、无权限授予,页面也访问不到 DSH 应用。
|
|
223
|
-
|
|
224
|
-
## 许可
|
|
18
|
+
- 只操作 **DSH 右侧栏原生浏览器**,不控制系统 Chrome/Safari,也不是无头浏览器。
|
|
19
|
+
- 页面内容操作需要 **DSH Desktop**(Electron 原生 webview);Web 版只能管理标签页。
|
|
20
|
+
- 截图仅当调用会话可见时可用;会话隐藏时截图会返回错误。
|
|
21
|
+
- guest 调用有超时:页面脚本 25s、导航 20s;页面卡死会返回错误而不是挂起,必要时重载标签页。
|
|
22
|
+
- 受 profile 配置约束:Host 可关闭工具入口、禁止 eval / 原生输入、限制允许访问的域名。
|
|
23
|
+
- 只能访问宿主侧栏浏览器允许的 HTTP(S) 页面,地址栏规则之外的目标不可达。
|
|
225
24
|
|
|
226
|
-
|
|
227
|
-
`@deepseek-ai/cosmokit` 同为 MIT,来源与改动见 `vendor/NOTICE.md`。
|
|
25
|
+
变更记录见 [CHANGELOG.md](CHANGELOG.md)。
|
package/lib/client.js
CHANGED
|
@@ -47,7 +47,7 @@ window.__ModuleLoader__.load({
|
|
|
47
47
|
const BASE_BACKOFF_MS = 400
|
|
48
48
|
const MAX_EVAL_CHARS = 100_000
|
|
49
49
|
/** Plugin version reported by diagnostics; keep in sync with package.json. */
|
|
50
|
-
const PLUGIN_VERSION = '0.2.
|
|
50
|
+
const PLUGIN_VERSION = '0.2.10'
|
|
51
51
|
const MAX_SCREENSHOT_WIDTH = 1_400
|
|
52
52
|
const MAX_SCREENSHOT_BASE64 = 12_000_000
|
|
53
53
|
/**
|
|
@@ -1136,6 +1136,9 @@ window.__ModuleLoader__.load({
|
|
|
1136
1136
|
const CONFIG_EN = {
|
|
1137
1137
|
'config.summary': 'Configure the sidebar browser tools: enabled switches, allowed hosts and permissions.',
|
|
1138
1138
|
'config.heading': 'Sidebar browser agent',
|
|
1139
|
+
'config.heroTitle': 'Hands on the real page inside the sidebar',
|
|
1140
|
+
'config.heroSubtitle': 'Read, click, type, scroll, run JavaScript and screenshot through the native Electron webview — no proxy, no Playwright.',
|
|
1141
|
+
'config.heroAlt': 'The agent drives the native sidebar browser through two tools.',
|
|
1139
1142
|
'config.intro': 'Applies immediately to this profile. Page content work needs the DSH Desktop native browser carrier.',
|
|
1140
1143
|
'config.enabled': 'Enable agent tools',
|
|
1141
1144
|
'config.enabledHint': 'Register sidebar_browser and sidebar_page for agents in this profile.',
|
|
@@ -1161,6 +1164,9 @@ window.__ModuleLoader__.load({
|
|
|
1161
1164
|
const CONFIG_ZH = {
|
|
1162
1165
|
'config.summary': '配置侧栏浏览器工具:启用开关、域名白名单与权限。',
|
|
1163
1166
|
'config.heading': '侧栏浏览器 Agent 控制',
|
|
1167
|
+
'config.heroTitle': '让 Agent 操作右侧栏里的真实网页',
|
|
1168
|
+
'config.heroSubtitle': '读取、点击、输入、滚动、执行 JavaScript、截图 —— 全部通过原生 Electron webview,无代理、无 Playwright。',
|
|
1169
|
+
'config.heroAlt': 'Agent 通过两个工具驱动侧栏原生浏览器。',
|
|
1164
1170
|
'config.intro': '修改立即对本 profile 生效。页面内容操作需要 DSH Desktop 原生浏览器载体。',
|
|
1165
1171
|
'config.enabled': '启用 Agent 工具',
|
|
1166
1172
|
'config.enabledHint': '为本 profile 中的 Agent 注册 sidebar_browser 与 sidebar_page。',
|
|
@@ -1182,6 +1188,75 @@ window.__ModuleLoader__.load({
|
|
|
1182
1188
|
'config.readOnlyHint': 'Host 不接受在此写入配置。',
|
|
1183
1189
|
}
|
|
1184
1190
|
|
|
1191
|
+
/** Inline hero diagram; kept in sync with `assets/how-it-works.svg`. */
|
|
1192
|
+
const HOW_IT_WORKS_SVG = `<svg width="720" height="230" viewBox="0 0 720 230" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
1193
|
+
<g>
|
|
1194
|
+
<defs>
|
|
1195
|
+
<linearGradient id="hiw-blue" x1="0" y1="0" x2="1" y2="1">
|
|
1196
|
+
<stop stop-color="#67C7FE"/><stop offset="1" stop-color="#3F77D8"/>
|
|
1197
|
+
</linearGradient>
|
|
1198
|
+
<linearGradient id="hiw-teal" x1="0" y1="0" x2="1" y2="1">
|
|
1199
|
+
<stop stop-color="#6AECB6"/><stop offset="1" stop-color="#37A6B1"/>
|
|
1200
|
+
</linearGradient>
|
|
1201
|
+
<linearGradient id="hiw-arrow" x1="260" y1="84" x2="336" y2="84" gradientUnits="userSpaceOnUse">
|
|
1202
|
+
<stop stop-color="#9AA7FF"/><stop offset="1" stop-color="#5F7BF0"/>
|
|
1203
|
+
</linearGradient>
|
|
1204
|
+
<filter id="hiw-shadow" x="-20%" y="-20%" width="140%" height="160%">
|
|
1205
|
+
<feDropShadow dx="0" dy="6" stdDeviation="10" flood-color="#4a3f8f" flood-opacity="0.10"/>
|
|
1206
|
+
</filter>
|
|
1207
|
+
</defs>
|
|
1208
|
+
<!-- agent tool chips -->
|
|
1209
|
+
<g filter="url(#hiw-shadow)">
|
|
1210
|
+
<rect x="0" y="26" width="250" height="48" rx="13" fill="#FFFFFF" stroke="#E4DFF5"/>
|
|
1211
|
+
<rect x="14" y="38" width="24" height="24" rx="7" fill="url(#hiw-blue)"/>
|
|
1212
|
+
<rect x="20" y="44" width="7" height="7" rx="2" fill="#FFFFFF" fill-opacity="0.95"/>
|
|
1213
|
+
<rect x="29" y="49" width="4" height="4" rx="1" fill="#FFFFFF" fill-opacity="0.55"/>
|
|
1214
|
+
<text x="50" y="56" font-family="SFMono-Regular, Menlo, monospace" font-size="15" fill="#3A3560">sidebar_browser</text>
|
|
1215
|
+
<rect x="14" y="88" width="24" height="24" rx="7" fill="url(#hiw-teal)"/>
|
|
1216
|
+
<rect x="20" y="94" width="9" height="3" rx="1.5" fill="#FFFFFF" fill-opacity="0.95"/>
|
|
1217
|
+
<rect x="20" y="100" width="6" height="3" rx="1.5" fill="#FFFFFF" fill-opacity="0.7"/>
|
|
1218
|
+
<rect x="20" y="106" width="7.5" height="3" rx="1.5" fill="#FFFFFF" fill-opacity="0.45"/>
|
|
1219
|
+
</g>
|
|
1220
|
+
<g filter="url(#hiw-shadow)">
|
|
1221
|
+
<rect x="0" y="88" width="250" height="48" rx="13" fill="#FFFFFF" stroke="#E4DFF5"/>
|
|
1222
|
+
<rect x="14" y="100" width="24" height="24" rx="7" fill="url(#hiw-teal)"/>
|
|
1223
|
+
<rect x="20" y="106" width="9" height="3" rx="1.5" fill="#FFFFFF" fill-opacity="0.95"/>
|
|
1224
|
+
<rect x="20" y="112" width="6" height="3" rx="1.5" fill="#FFFFFF" fill-opacity="0.7"/>
|
|
1225
|
+
<rect x="20" y="118" width="7.5" height="3" rx="1.5" fill="#FFFFFF" fill-opacity="0.45"/>
|
|
1226
|
+
<text x="50" y="118" font-family="SFMono-Regular, Menlo, monospace" font-size="15" fill="#3A3560">sidebar_page</text>
|
|
1227
|
+
</g>
|
|
1228
|
+
<!-- arrow -->
|
|
1229
|
+
<path d="M266 82H322" stroke="url(#hiw-arrow)" stroke-width="3" stroke-linecap="round"/>
|
|
1230
|
+
<path d="M318 74L330 82L318 90" stroke="#5F7BF0" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
|
1231
|
+
<!-- browser window -->
|
|
1232
|
+
<g filter="url(#hiw-shadow)">
|
|
1233
|
+
<rect x="342" y="4" width="374" height="222" rx="18" fill="#FFFFFF" stroke="#E4DFF5"/>
|
|
1234
|
+
<rect x="342" y="4" width="374" height="34" rx="18" fill="#FAF8FF"/>
|
|
1235
|
+
<rect x="342" y="21" width="374" height="17" fill="#FAF8FF"/>
|
|
1236
|
+
<circle cx="360" cy="21" r="4" fill="#D9D4EC"/>
|
|
1237
|
+
<circle cx="372" cy="21" r="4" fill="#D9D4EC"/>
|
|
1238
|
+
<circle cx="384" cy="21" r="4" fill="#D9D4EC"/>
|
|
1239
|
+
<rect x="402" y="13" width="196" height="16" rx="8" fill="#F0EDFA"/>
|
|
1240
|
+
<rect x="414" y="18" width="92" height="6" rx="3" fill="#C9C3E4"/>
|
|
1241
|
+
<!-- sidebar list -->
|
|
1242
|
+
<rect x="354" y="48" width="74" height="166" rx="12" fill="#F6F3FE"/>
|
|
1243
|
+
<rect x="364" y="60" width="54" height="9" rx="4.5" fill="url(#hiw-teal)" fill-opacity="0.9"/>
|
|
1244
|
+
<rect x="364" y="78" width="42" height="9" rx="4.5" fill="#CFC8EC"/>
|
|
1245
|
+
<rect x="364" y="96" width="48" height="9" rx="4.5" fill="#DCD6F1"/>
|
|
1246
|
+
<rect x="364" y="114" width="38" height="9" rx="4.5" fill="#DCD6F1"/>
|
|
1247
|
+
<!-- page -->
|
|
1248
|
+
<rect x="440" y="48" width="264" height="166" rx="12" fill="#FBFAFF" stroke="#EFEBFA"/>
|
|
1249
|
+
<rect x="456" y="64" width="132" height="10" rx="5" fill="#E4DEF6"/>
|
|
1250
|
+
<rect x="456" y="84" width="176" height="10" rx="5" fill="#EDE8F8"/>
|
|
1251
|
+
<rect x="456" y="104" width="112" height="10" rx="5" fill="#EDE8F8"/>
|
|
1252
|
+
<rect x="456" y="128" width="104" height="22" rx="11" fill="#E2F7EE" stroke="#7CDDB4" stroke-opacity="0.7"/>
|
|
1253
|
+
<rect x="468" y="137" width="66" height="6" rx="3" fill="#37A6B1" fill-opacity="0.55"/>
|
|
1254
|
+
<!-- cursor -->
|
|
1255
|
+
<path d="M566 128L566 158L574 150L580 161L588 157L581 146L593 145Z" fill="url(#hiw-blue)" stroke="#FFFFFF" stroke-width="1.6" stroke-linejoin="round"/>
|
|
1256
|
+
</g>
|
|
1257
|
+
</g>
|
|
1258
|
+
</svg>`
|
|
1259
|
+
|
|
1185
1260
|
const configStyles = {
|
|
1186
1261
|
root: { display: 'flex', flexDirection: 'column', gap: '18px', maxWidth: '620px', color: 'var(--dsw-alias-label-primary)', fontSize: '13px', lineHeight: '20px' },
|
|
1187
1262
|
heading: { margin: 0, fontSize: '15px', fontWeight: 600, lineHeight: '22px' },
|
|
@@ -1226,13 +1301,35 @@ window.__ModuleLoader__.load({
|
|
|
1226
1301
|
cursor: 'pointer',
|
|
1227
1302
|
font: 'inherit',
|
|
1228
1303
|
},
|
|
1304
|
+
// Hero illustration shown above the official settings form. The SVG is
|
|
1305
|
+
// inline so it travels with the installed bundle and needs no asset route.
|
|
1306
|
+
heroCard: {
|
|
1307
|
+
margin: '0 0 20px',
|
|
1308
|
+
padding: '24px 26px',
|
|
1309
|
+
border: '0.5px solid var(--dsw-alias-border-l1)',
|
|
1310
|
+
borderRadius: 'var(--dsw-radius-xl, 14px)',
|
|
1311
|
+
background: 'linear-gradient(135deg, color-mix(in srgb, var(--dsw-alias-brand-primary) 6%, var(--dsw-alias-bg-layer-1)), color-mix(in srgb, var(--dsw-alias-brand-primary) 14%, var(--dsw-alias-bg-layer-1)))',
|
|
1312
|
+
},
|
|
1313
|
+
heroTitle: { margin: '0 0 6px', color: 'var(--dsw-alias-label-primary)', fontSize: '18px', fontWeight: 600, lineHeight: 1.4 },
|
|
1314
|
+
heroSubtitle: { margin: '0 0 16px', color: 'var(--dsw-alias-label-secondary)', fontSize: '13px', lineHeight: 1.5 },
|
|
1315
|
+
heroDiagram: { display: 'block', width: '100%', height: 'auto' },
|
|
1229
1316
|
// --- official-style settings page (shared SettingsForm + Switch) ---
|
|
1230
|
-
|
|
1317
|
+
// Mirrors the built-in settings page rhythm: the shared form only stacks
|
|
1318
|
+
// children, so each block below brings its own 12px vertical padding, the
|
|
1319
|
+
// same scale the official fields and rows use.
|
|
1320
|
+
formBody: { display: 'flex', flexDirection: 'column' },
|
|
1321
|
+
noticeOfficial: {
|
|
1322
|
+
margin: '0 0 4px',
|
|
1323
|
+
color: 'var(--dsw-alias-label-secondary)',
|
|
1324
|
+
fontSize: '13px',
|
|
1325
|
+
lineHeight: 1.5,
|
|
1326
|
+
},
|
|
1327
|
+
switchList: { display: 'flex', flexDirection: 'column', padding: '12px 0' },
|
|
1231
1328
|
switchRow: {
|
|
1232
1329
|
display: 'flex',
|
|
1233
1330
|
alignItems: 'center',
|
|
1234
1331
|
gap: '16px',
|
|
1235
|
-
minHeight: '
|
|
1332
|
+
minHeight: '40px',
|
|
1236
1333
|
padding: '8px 0',
|
|
1237
1334
|
borderBottom: '0.5px solid var(--dsw-alias-border-l2)',
|
|
1238
1335
|
},
|
|
@@ -1240,28 +1337,29 @@ window.__ModuleLoader__.load({
|
|
|
1240
1337
|
display: 'flex',
|
|
1241
1338
|
alignItems: 'center',
|
|
1242
1339
|
gap: '16px',
|
|
1243
|
-
minHeight: '
|
|
1340
|
+
minHeight: '40px',
|
|
1244
1341
|
padding: '8px 0',
|
|
1245
1342
|
},
|
|
1246
1343
|
switchText: { display: 'flex', flexDirection: 'column', gap: '2px', flex: '1 1 auto', minWidth: 0 },
|
|
1247
|
-
switchLabel: { color: 'var(--dsw-alias-label-primary)', fontSize: '13px', lineHeight:
|
|
1248
|
-
switchHint: { color: 'var(--dsw-alias-label-
|
|
1249
|
-
|
|
1344
|
+
switchLabel: { color: 'var(--dsw-alias-label-primary)', fontSize: '13px', lineHeight: 1.5, fontWeight: 500 },
|
|
1345
|
+
switchHint: { color: 'var(--dsw-alias-label-tertiary)', fontSize: '12px', lineHeight: 1.5 },
|
|
1346
|
+
field: { display: 'flex', flexDirection: 'column', gap: '6px', padding: '12px 0' },
|
|
1347
|
+
fieldLabel: { color: 'var(--dsw-alias-label-primary)', fontSize: '13px', lineHeight: 1.5, fontWeight: 500 },
|
|
1348
|
+
fieldHint: { color: 'var(--dsw-alias-label-tertiary)', fontSize: '12px', lineHeight: 1.5 },
|
|
1250
1349
|
textareaOfficial: {
|
|
1251
1350
|
boxSizing: 'border-box',
|
|
1252
1351
|
width: '100%',
|
|
1253
1352
|
minHeight: '96px',
|
|
1254
1353
|
color: 'var(--dsw-alias-label-primary)',
|
|
1255
|
-
background: 'var(--dsw-alias-bg-
|
|
1354
|
+
background: 'var(--dsw-alias-bg-layer-3)',
|
|
1256
1355
|
border: '0.5px solid var(--dsw-alias-border-l4)',
|
|
1257
1356
|
borderRadius: 'var(--dsw-radius-md, 8px)',
|
|
1258
1357
|
font: 'inherit',
|
|
1259
1358
|
fontSize: '13px',
|
|
1260
|
-
lineHeight:
|
|
1261
|
-
padding: '8px
|
|
1359
|
+
lineHeight: 1.5,
|
|
1360
|
+
padding: '8px 12px',
|
|
1262
1361
|
resize: 'vertical',
|
|
1263
1362
|
},
|
|
1264
|
-
noticeOfficial: { color: 'var(--dsw-alias-label-secondary)', margin: 0, fontSize: '13px', lineHeight: '20px' },
|
|
1265
1363
|
}
|
|
1266
1364
|
|
|
1267
1365
|
/**
|
|
@@ -1378,7 +1476,7 @@ window.__ModuleLoader__.load({
|
|
|
1378
1476
|
switchRow(t, { label: 'config.enabled', hint: 'config.enabledHint', checked: draft.enabled, disabled: readOnly, onChange: (next) => setDraft({ ...draft, enabled: next }) }),
|
|
1379
1477
|
React.createElement('div', { style: configStyles.field },
|
|
1380
1478
|
React.createElement('span', { style: configStyles.checkLabel }, t('config.hosts')),
|
|
1381
|
-
React.createElement('span', { style: configStyles.
|
|
1479
|
+
React.createElement('span', { style: configStyles.fieldHint }, t('config.hostsHint')),
|
|
1382
1480
|
React.createElement('textarea', {
|
|
1383
1481
|
style: configStyles.textarea,
|
|
1384
1482
|
rows: 4,
|
|
@@ -1492,13 +1590,26 @@ window.__ModuleLoader__.load({
|
|
|
1492
1590
|
}),
|
|
1493
1591
|
)
|
|
1494
1592
|
return React.createElement(SettingsForm, { labels, state: model, onSave: save, onDiscard: discard },
|
|
1495
|
-
React.createElement('
|
|
1496
|
-
|
|
1593
|
+
React.createElement('div', { style: configStyles.formBody },
|
|
1594
|
+
React.createElement('div', {
|
|
1595
|
+
style: configStyles.heroCard,
|
|
1596
|
+
role: 'img',
|
|
1597
|
+
'aria-label': t('config.heroAlt'),
|
|
1598
|
+
},
|
|
1599
|
+
React.createElement('h3', { style: configStyles.heroTitle }, t('config.heroTitle')),
|
|
1600
|
+
React.createElement('p', { style: configStyles.heroSubtitle }, t('config.heroSubtitle')),
|
|
1601
|
+
React.createElement('div', {
|
|
1602
|
+
style: configStyles.heroDiagram,
|
|
1603
|
+
dangerouslySetInnerHTML: { __html: HOW_IT_WORKS_SVG },
|
|
1604
|
+
}),
|
|
1605
|
+
),
|
|
1606
|
+
React.createElement('p', { style: configStyles.noticeOfficial }, t('config.intro')),
|
|
1607
|
+
React.createElement('div', { style: configStyles.switchList },
|
|
1497
1608
|
toggleRow('enabled', 'config.enabled', 'config.enabledHint', false),
|
|
1498
1609
|
toggleRow('allowEval', 'config.allowEval', 'config.allowEvalHint', false),
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1610
|
+
toggleRow('allowNativeInput', 'config.allowNativeInput', 'config.allowNativeInputHint', true),
|
|
1611
|
+
),
|
|
1612
|
+
React.createElement('div', { style: configStyles.field },
|
|
1502
1613
|
React.createElement('span', { style: configStyles.fieldLabel }, t('config.hosts')),
|
|
1503
1614
|
React.createElement('textarea', {
|
|
1504
1615
|
style: configStyles.textareaOfficial,
|
|
@@ -1509,7 +1620,8 @@ window.__ModuleLoader__.load({
|
|
|
1509
1620
|
value: draft.hostsText,
|
|
1510
1621
|
onChange: (event) => setDraft({ ...draft, hostsText: event.target.value }),
|
|
1511
1622
|
}),
|
|
1512
|
-
|
|
1623
|
+
React.createElement('span', { style: configStyles.fieldHint }, t('config.hostsHint')),
|
|
1624
|
+
),
|
|
1513
1625
|
),
|
|
1514
1626
|
)
|
|
1515
1627
|
}
|
|
@@ -20,7 +20,7 @@ import z from '../vendor/schemastery/index.mjs'
|
|
|
20
20
|
export const name = 'dsh-sidebar-browser-agent'
|
|
21
21
|
|
|
22
22
|
/** Plugin version reported by diagnostics; keep in sync with package.json. */
|
|
23
|
-
export const PLUGIN_VERSION = '0.2.
|
|
23
|
+
export const PLUGIN_VERSION = '0.2.10'
|
|
24
24
|
|
|
25
25
|
/** Services that must exist before this plugin mounts. */
|
|
26
26
|
export const inject = ['webServer', 'connection', 'tools']
|
package/lib/index.js
CHANGED
|
@@ -20,7 +20,7 @@ import z from '../vendor/schemastery/index.mjs'
|
|
|
20
20
|
export const name = 'dsh-sidebar-browser-agent'
|
|
21
21
|
|
|
22
22
|
/** Plugin version reported by diagnostics; keep in sync with package.json. */
|
|
23
|
-
export const PLUGIN_VERSION = '0.2.
|
|
23
|
+
export const PLUGIN_VERSION = '0.2.10'
|
|
24
24
|
|
|
25
25
|
/** Services that must exist before this plugin mounts. */
|
|
26
26
|
export const inject = ['webServer', 'connection', 'tools']
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-sidebar-browser-agent",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"icon": "./icon.svg",
|
|
6
6
|
"description": "Native agent control for the DSH right-sidebar browser: manage tabs, and read, click, type, scroll, run JavaScript in and screenshot the real page through its Electron guest.",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"electron-webview"
|
|
21
21
|
],
|
|
22
22
|
"exports": {
|
|
23
|
-
".": "./lib/entry-0.2.
|
|
23
|
+
".": "./lib/entry-0.2.10.js",
|
|
24
24
|
"./client": "./lib/client.js",
|
|
25
25
|
"./locale/*.json": "./locale/*.json",
|
|
26
26
|
"./icon": "./icon.svg"
|
package/test/client-harness.mjs
CHANGED
|
@@ -538,6 +538,10 @@ async function main() {
|
|
|
538
538
|
// --- configuration page: locale, slot registration and rendering ---
|
|
539
539
|
const localeEntry = localeRegistrations.find((entry) => entry.ns === 'sidebarBrowserAgent')
|
|
540
540
|
check('config locale registered', localeEntry !== undefined && localeEntry.dicts.en !== undefined && localeEntry.dicts.zh !== undefined, localeRegistrations.map((entry) => entry.ns))
|
|
541
|
+
check('config locale carries the hero copy',
|
|
542
|
+
localeEntry.dicts.en['config.heroTitle'] !== undefined && localeEntry.dicts.zh['config.heroTitle'] !== undefined
|
|
543
|
+
&& localeEntry.dicts.en['config.heroSubtitle'] !== undefined && localeEntry.dicts.zh['config.heroSubtitle'] !== undefined,
|
|
544
|
+
Object.keys(localeEntry.dicts.en).filter((key) => key.startsWith('config.hero')))
|
|
541
545
|
const slotEntry = slotRegistrations.find((entry) => entry.options?.name === 'plugins.row.config')
|
|
542
546
|
check('row config slot registered', slotEntry !== undefined
|
|
543
547
|
&& slotEntry.options.key === 'dsh-sidebar-browser-agent#sidebar-browser-agent'
|