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 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
- # 本地笔记本:把 bridge 端口 + dev server 端口一起转发到本地 127.0.0.1
46
- ui-inspector link <remote-ssh-目标> --port <dev端口>
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端口> -L <dev端口>:127.0.0.1:<dev端口> <remote>`,保持前台、Ctrl-C 断开。远程 bridge 端口不必和本地一致:本地用 `--bridge-port`、远程用 `--remote-bridge-port`(默认两者相同);弹窗里填的是**本地**那个端口。
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 <devport> # 本地:SSH 把 bridge 端口 + dev 端口转发到本地 127.0.0.1
8
- * ui-inspector doctor <remote> --port <devport> # 本地:自检远程链路断在哪层并打印恢复命令
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 端口 + dev server 端口都转发到本地 127.0.0.1。
137
- // 远程 bridge 监听端口可与本地不同:-L <本地bridge端口>:127.0.0.1:<远程bridge端口>。
138
- function link({remote, devPort, bridgePort, remoteBridgePort}) {
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
- '用法:ui-inspector link <remote-ssh-目标> --port <dev端口> [--bridge-port 8917] [--remote-bridge-port <远程端口>]'
142
- );
143
- out('例: ui-inspector link dev-box --port 5173');
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
- ` bridge → 本地 127.0.0.1:${bridgePort} ⇢ 远程 127.0.0.1:${rbp}${devPort ? `;dev → 127.0.0.1:${devPort}` : ''}`
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, devPort, bridgePort, remoteBridgePort};
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, devPort, bridgePort} = parseLink();
258
+ const {remote, bridgePort, remoteBridgePort, devPort} = parseLink();
266
259
  const rm = remote || '<remote>';
267
- const dp = devPort || '<dev端口>';
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(` 恢复:ui-inspector link ${rm} --port ${dp}`);
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} 不可达 → 隧道没转发该端口,或远程 dev server 没起`);
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 端口 + dev 端口转发到本地(--port <dev端口> [--bridge-port 8917])');
304
- out(' doctor <remote> 本地:自检远程开发链路断在哪层并打印恢复命令(--port <dev端口> [--bridge-port 8917])');
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
- for (var i = 0; i < selected.length; i++) {
131
- placeBox(selected[i].box, selected[i].el);
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) {
@@ -106,7 +106,7 @@
106
106
  <input
107
107
  id="domain-input"
108
108
  type="text"
109
- placeholder="如 https://local.oreateai.com"
109
+ placeholder="如 https://example.com"
110
110
  />
111
111
  <button
112
112
  id="add-domain"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "comate-ui-inspector",
3
- "version": "0.1.2-alpha.6",
3
+ "version": "0.1.3",
4
4
  "description": "按住 Alt 点选页面元素,把「元素 + Vue 组件源码路径」交给 Comate 定位源码修改。浏览器扩展 + Native Messaging host + MCP + hook。",
5
5
  "type": "module",
6
6
  "license": "MIT",