comate-ui-inspector 0.1.2-alpha.6 → 0.1.3
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/README.md +6 -4
- package/cli.mjs +29 -35
- package/extension/inspector-main.js +31 -2
- package/extension/popup.html +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -42,16 +42,18 @@ ui-inspector install # 注册 native host + 个人级 hook,并打印
|
|
|
42
42
|
```bash
|
|
43
43
|
# 远程机上(和 Comate hook/MCP 同机):装 hook/MCP 并后台起 bridge
|
|
44
44
|
ui-inspector install --remote # --port 默认 8917;跳过无浏览器的 native host
|
|
45
|
-
#
|
|
46
|
-
ui-inspector link <remote-ssh-目标>
|
|
45
|
+
# 本地笔记本:把远程 bridge 端口转发到本地 127.0.0.1(dev server 端口自行转发)
|
|
46
|
+
ui-inspector link <remote-ssh-目标> # 本地/远程 bridge 都用默认 8917
|
|
47
|
+
# 本地与远程 bridge 端口不同时才两个都填:
|
|
48
|
+
ui-inspector link <remote-ssh-目标> --bridge-port <本地> --remote-bridge-port <远程>
|
|
47
49
|
```
|
|
48
50
|
|
|
49
|
-
`install --remote` 会打印 MCP 配置(贴进**远程**的 Comate 并 reload),并把 bridge 后台拉起(日志在 `~/.ui-inspector/bridge.log`)。`link` 本质是 `ssh -N -L <本地bridge端口>:127.0.0.1:<远程bridge端口>
|
|
51
|
+
`install --remote` 会打印 MCP 配置(贴进**远程**的 Comate 并 reload),并把 bridge 后台拉起(日志在 `~/.ui-inspector/bridge.log`)。`link` 本质是 `ssh -N -L <本地bridge端口>:127.0.0.1:<远程bridge端口> <remote>`,保持前台、Ctrl-C 断开。端口参数只有两个:本地 `--bridge-port`、远程 `--remote-bridge-port`(默认相同,相同时只填 `--bridge-port`、省略即默认 8917);弹窗里填的是**本地**那个。dev server 端口由你的远程开发工具自行转发,`link` 不再代管。
|
|
50
52
|
|
|
51
53
|
链路断了(ssh 掉线 / 远程 bridge 或 dev server 停了)时,先在本地自检:
|
|
52
54
|
|
|
53
55
|
```bash
|
|
54
|
-
ui-inspector doctor <remote> --port <dev端口> # 定位断在哪层并打印对应恢复命令
|
|
56
|
+
ui-inspector doctor <remote> --dev-port <dev端口> # 定位断在哪层并打印对应恢复命令
|
|
55
57
|
```
|
|
56
58
|
|
|
57
59
|
恢复顺序:先确保远程 bridge 活(`ui-inspector bridge` / `install --remote`)→ 再接本地隧道(`ui-inspector link`)→ 再看远程 dev server。
|
package/cli.mjs
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* npm i -g comate-ui-inspector
|
|
5
5
|
* ui-inspector install # 注册 native host + 写个人级 hook + 打印 MCP 配置与扩展目录
|
|
6
6
|
* ui-inspector install --remote # 远程开发机:装 hook/MCP 并后台起 bridge(跳过无浏览器的 native host)
|
|
7
|
-
* ui-inspector link <remote> --port
|
|
8
|
-
* ui-inspector doctor <remote> --port <
|
|
7
|
+
* ui-inspector link <remote> --bridge-port <本地> [--remote-bridge-port <远程>] # 本地:SSH 把 bridge 端口转发到本地 127.0.0.1
|
|
8
|
+
* ui-inspector doctor <remote> --bridge-port <本地> [--dev-port <dev端口>] # 本地:自检远程链路断在哪层并打印恢复命令
|
|
9
9
|
* ui-inspector uninstall # 移除 native host 清单 + 个人级 hook(--purge 连 ~/.ui-inspector 一起删)
|
|
10
10
|
* ui-inspector status # 查看安装状态
|
|
11
11
|
* ui-inspector bridge # 启动 HTTP bridge(远程开发用,--port 默认 8917)
|
|
@@ -133,29 +133,22 @@ function install({remote, port}) {
|
|
|
133
133
|
out('完成后:按住 Alt 点元素多选 → 回 Comate 写修改意见。');
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
// 本地笔记本:把远程 bridge
|
|
137
|
-
//
|
|
138
|
-
|
|
136
|
+
// 本地笔记本:把远程 bridge 端口转发到本地 127.0.0.1。端口参数只有两个:
|
|
137
|
+
// --bridge-port <本地>(默认 8917)、--remote-bridge-port <远程>(默认与本地相同)。
|
|
138
|
+
// 本地=远程时只填 --bridge-port;不同才两个都填。dev server 端口自行转发。
|
|
139
|
+
function link({remote, bridgePort, remoteBridgePort}) {
|
|
139
140
|
if (!remote) {
|
|
140
|
-
out(
|
|
141
|
-
|
|
142
|
-
);
|
|
143
|
-
out('
|
|
141
|
+
out('用法:ui-inspector link <remote-ssh-目标> [--bridge-port <本地端口>] [--remote-bridge-port <远程端口>]');
|
|
142
|
+
out('例: ui-inspector link dev-box # 本地/远程都用默认 8917');
|
|
143
|
+
out(' ui-inspector link dev-box --bridge-port 9000 # 本地=远程=9000');
|
|
144
|
+
out(' ui-inspector link dev-box --bridge-port 9000 --remote-bridge-port 8917 # 本地 9000 → 远程 8917');
|
|
144
145
|
return;
|
|
145
146
|
}
|
|
146
147
|
const rbp = remoteBridgePort || bridgePort;
|
|
147
|
-
const args = ['-N', '-L', `${bridgePort}:127.0.0.1:${rbp}
|
|
148
|
-
if (devPort) {
|
|
149
|
-
args.push('-L', `${devPort}:127.0.0.1:${devPort}`);
|
|
150
|
-
}
|
|
151
|
-
args.push(remote);
|
|
148
|
+
const args = ['-N', '-L', `${bridgePort}:127.0.0.1:${rbp}`, remote];
|
|
152
149
|
out(`转发中(Ctrl-C 断开):ssh ${args.join(' ')}`);
|
|
153
|
-
out(
|
|
154
|
-
|
|
155
|
-
);
|
|
156
|
-
out(
|
|
157
|
-
` 提示:弹窗里的端口填本地的 ${bridgePort};远程 bridge 需在 ${rbp} 上监听(ui-inspector bridge --port ${rbp})。`
|
|
158
|
-
);
|
|
150
|
+
out(` bridge → 本地 127.0.0.1:${bridgePort} ⇢ 远程 127.0.0.1:${rbp}(弹窗里填本地 ${bridgePort};远程 bridge 需在 ${rbp} 监听)`);
|
|
151
|
+
out(` 注:dev server 端口请自行转发(远程开发工具通常已转发,或另跑 ssh -L <dev>:127.0.0.1:<dev> ${remote})`);
|
|
159
152
|
const child = spawn('ssh', args, {stdio: 'inherit'});
|
|
160
153
|
child.on('exit', code => process.exit(code ?? 0));
|
|
161
154
|
}
|
|
@@ -226,22 +219,22 @@ function getOpt(name, def) {
|
|
|
226
219
|
function parseLink() {
|
|
227
220
|
const rest = process.argv.slice(3);
|
|
228
221
|
let remote;
|
|
229
|
-
let devPort;
|
|
230
222
|
let bridgePort = '8917';
|
|
231
223
|
let remoteBridgePort;
|
|
224
|
+
let devPort;
|
|
232
225
|
for (let i = 0; i < rest.length; i++) {
|
|
233
226
|
const a = rest[i];
|
|
234
|
-
if (a === '--port') {
|
|
235
|
-
devPort = rest[++i];
|
|
236
|
-
} else if (a === '--bridge-port') {
|
|
227
|
+
if (a === '--bridge-port') {
|
|
237
228
|
bridgePort = rest[++i];
|
|
238
229
|
} else if (a === '--remote-bridge-port') {
|
|
239
230
|
remoteBridgePort = rest[++i];
|
|
231
|
+
} else if (a === '--dev-port') {
|
|
232
|
+
devPort = rest[++i]; // 仅 doctor 用;放这里一并消费,避免其值被当成 remote
|
|
240
233
|
} else if (!a.startsWith('-')) {
|
|
241
234
|
remote = a;
|
|
242
235
|
}
|
|
243
236
|
}
|
|
244
|
-
return {remote,
|
|
237
|
+
return {remote, bridgePort, remoteBridgePort, devPort};
|
|
245
238
|
}
|
|
246
239
|
|
|
247
240
|
// 探测本地某端口的 http 可达性(2s 超时)。用 node:http 而非 fetch,兼容 node 18+。
|
|
@@ -262,9 +255,12 @@ function probe(port, path) {
|
|
|
262
255
|
|
|
263
256
|
// 一键自检:定位远程开发链路(本地隧道 → 远程 bridge → 远程 dev server)断在哪层,并打印恢复命令。
|
|
264
257
|
async function doctor() {
|
|
265
|
-
const {remote,
|
|
258
|
+
const {remote, bridgePort, remoteBridgePort, devPort} = parseLink();
|
|
266
259
|
const rm = remote || '<remote>';
|
|
267
|
-
const
|
|
260
|
+
const linkCmd =
|
|
261
|
+
`ui-inspector link ${rm}` +
|
|
262
|
+
(bridgePort !== '8917' ? ` --bridge-port ${bridgePort}` : '') +
|
|
263
|
+
(remoteBridgePort && remoteBridgePort !== bridgePort ? ` --remote-bridge-port ${remoteBridgePort}` : '');
|
|
268
264
|
out('ui-inspector doctor(远程开发链路自检):');
|
|
269
265
|
|
|
270
266
|
const b = await probe(bridgePort, '/state');
|
|
@@ -272,7 +268,7 @@ async function doctor() {
|
|
|
272
268
|
out(` [1] bridge ✓ 127.0.0.1:${bridgePort} 可达——本地→隧道→远程 bridge 全通`);
|
|
273
269
|
} else if (b.state === 'refused') {
|
|
274
270
|
out(` [1] bridge ✗ 本地 127.0.0.1:${bridgePort} 无人监听 → SSH 隧道断了(或没起)`);
|
|
275
|
-
out(`
|
|
271
|
+
out(` 恢复:${linkCmd}`);
|
|
276
272
|
} else {
|
|
277
273
|
out(` [1] bridge ✗ 隧道在、但远程 bridge 无响应(${b.state})`);
|
|
278
274
|
out(` 恢复:登远程机跑 ui-inspector bridge(或 install --remote),看 ~/.ui-inspector/bridge.log`);
|
|
@@ -281,27 +277,25 @@ async function doctor() {
|
|
|
281
277
|
if (devPort) {
|
|
282
278
|
const d = await probe(devPort, '/');
|
|
283
279
|
if (d.state === 'refused') {
|
|
284
|
-
out(` [2] dev ✗ 本地 127.0.0.1:${devPort} 不可达 →
|
|
285
|
-
out(` 恢复:① 远程起 dev server;② 本地 ui-inspector link ${rm} --port ${devPort}`);
|
|
280
|
+
out(` [2] dev ✗ 本地 127.0.0.1:${devPort} 不可达 → 该端口没转发,或远程 dev server 没起`);
|
|
286
281
|
} else if (d.state === 'timeout') {
|
|
287
282
|
out(` [2] dev ✗ 127.0.0.1:${devPort} 超时 → 远程 dev server 可能挂了`);
|
|
288
|
-
out(` 恢复:远程重启 dev server`);
|
|
289
283
|
} else {
|
|
290
284
|
out(` [2] dev ✓ 127.0.0.1:${devPort} 可达(HTTP ${d.code})`);
|
|
291
285
|
}
|
|
292
286
|
} else {
|
|
293
|
-
out(' [2] dev - 未传 --port,跳过 dev server 检查');
|
|
287
|
+
out(' [2] dev - 未传 --dev-port,跳过 dev server 检查');
|
|
294
288
|
}
|
|
295
289
|
|
|
296
|
-
out('\n恢复顺序:先确保远程 bridge 活 → 再接本地隧道(link)→ 再看远程 dev server
|
|
290
|
+
out('\n恢复顺序:先确保远程 bridge 活 → 再接本地隧道(link)→ 再看远程 dev server(自行转发)。');
|
|
297
291
|
}
|
|
298
292
|
|
|
299
293
|
function help() {
|
|
300
294
|
out('用法:ui-inspector <install|uninstall|status|bridge|link|doctor>');
|
|
301
295
|
out(' install 注册 native host + 个人级 hook,并打印 MCP 配置与扩展目录');
|
|
302
296
|
out(' install --remote 远程开发机:装 hook/MCP 并后台起 bridge(跳过 native host,--port 默认 8917)');
|
|
303
|
-
out(' link <remote> 本地:SSH 把 bridge
|
|
304
|
-
out(' doctor <remote>
|
|
297
|
+
out(' link <remote> 本地:SSH 把 bridge 端口转发到本地(--bridge-port <本地> [--remote-bridge-port <远程>])');
|
|
298
|
+
out(' doctor <remote> 本地:自检远程链路断在哪层并打印恢复命令(--bridge-port <本地> [--dev-port <dev端口>])');
|
|
305
299
|
out(' uninstall 移除上述注册(加 --purge 连 ~/.ui-inspector 数据一起删)');
|
|
306
300
|
out(' status 查看安装状态');
|
|
307
301
|
out(' bridge 启动 HTTP bridge(远程开发用,--port 默认 8917)');
|
|
@@ -126,9 +126,30 @@
|
|
|
126
126
|
box.style.height = rect.height + 'px';
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
// 元素还在页面上渲染吗:被移出 DOM 或 display:none 时 getClientRects() 为空。
|
|
130
|
+
function isRendered(el) {
|
|
131
|
+
return el && el.isConnected && el.getClientRects().length > 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// 每帧跟随元素位置;同时剔除已消失(被删除/隐藏)的选中项,连同高亮框一起移除。
|
|
129
135
|
function repositionBoxes() {
|
|
130
|
-
|
|
131
|
-
|
|
136
|
+
var removed = 0;
|
|
137
|
+
for (var i = selected.length - 1; i >= 0; i--) {
|
|
138
|
+
if (isRendered(selected[i].el)) {
|
|
139
|
+
placeBox(selected[i].box, selected[i].el);
|
|
140
|
+
} else {
|
|
141
|
+
selected[i].box.remove();
|
|
142
|
+
selected.splice(i, 1);
|
|
143
|
+
removed++;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (removed) {
|
|
147
|
+
postSelection();
|
|
148
|
+
showHint(
|
|
149
|
+
selected.length
|
|
150
|
+
? '有选中元素已消失,已移除(当前 ' + selected.length + ' 个)'
|
|
151
|
+
: '选中元素已消失,已全部移除'
|
|
152
|
+
);
|
|
132
153
|
}
|
|
133
154
|
}
|
|
134
155
|
|
|
@@ -233,6 +254,14 @@
|
|
|
233
254
|
window.addEventListener('scroll', repositionBoxes, true);
|
|
234
255
|
window.addEventListener('resize', repositionBoxes, true);
|
|
235
256
|
|
|
257
|
+
// 每帧跟随并清理:元素被删/隐藏后,repositionBoxes 会移除它的高亮框(scroll/resize 不一定触发)。
|
|
258
|
+
(function raf() {
|
|
259
|
+
if (selected.length) {
|
|
260
|
+
repositionBoxes();
|
|
261
|
+
}
|
|
262
|
+
window.requestAnimationFrame(raf);
|
|
263
|
+
})();
|
|
264
|
+
|
|
236
265
|
// bridge-content 轮询回发的消息:cleared(撤高亮)/ status(toast 提示)
|
|
237
266
|
window.addEventListener('message', function (event) {
|
|
238
267
|
if (event.source !== window || !event.data) {
|
package/extension/popup.html
CHANGED
package/package.json
CHANGED