mirror of
https://github.com/kuaifan/dootask.git
synced 2026-06-27 17:52:12 +00:00
页面操作(doo page → /ws → 浮窗 operation-module → action-executor)原用手写 iView-only 选择器 + 朴素 value= 赋值,在 React+Radix 同源 iframe 业务表单上几乎 填不进。重构为「复用 Playwright 注入脚本的描述层 + 按环境选后端的执行层」: 描述层(共用): - 新增 aria/aria-bundle.js,vendoring Playwright ariaSnapshot/roleUtils/domUtils 子集预构建为 ESM(Apache-2.0 NOTICE + 固定上游 commit),产出 YAML 快照与活的 ref→Element Map。 - page-context-collector 改用 buildSnapshot 采集,operation-module 经 setRefMap 以 Map 直接持有元素取代 selector 反查。 执行层(按环境选后端,失败回退): - web-backend:原生 prototype value setter + beforeinput/input/change/blur 序列 + 回读校验(不符报 value_not_applied,绝不假报成功);自定义下拉/日期 open→click。 - electron-backend + electron/lib/page-input.js:经 webContents.debugger 走 CDP Input 域产生 isTrusted=true 可信输入(insertText/dispatchMouseEvent/KeyEvent), 任一步失败回退 Web 后端。 - 复杂控件(动态明细表/成员选择器/文件上传)如实返回 unsupported_widget。 协议与链路(doo page、active-context 失效守卫、导航类 action)保持不变。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
58 lines
2.3 KiB
JavaScript
Vendored
58 lines
2.3 KiB
JavaScript
Vendored
// AI 助手页面操作 · CDP 可信输入(主进程)
|
||
//
|
||
// 渲染端经 window.electron.sendAsync('pageInput', {op, ...}) 调用(preload 已有的
|
||
// invoke 通道);这里用 webContents.debugger 走 CDP Input 域,产生 isTrusted=true 的
|
||
// 可信输入,等价 Playwright 真正可靠的那一半,但第一方、在用户会话内,且同源 iframe 可达。
|
||
//
|
||
// 目标 webContents = event.sender(发起的渲染进程;同源 iframe 在其内,视口坐标可达)。
|
||
// 任一步骤失败抛错 → 渲染端 electron-backend 捕获后回退 Web 后端。
|
||
|
||
const { ipcMain } = require('electron')
|
||
|
||
const attached = new WeakSet()
|
||
|
||
function ensureAttached(wc) {
|
||
if (attached.has(wc)) return
|
||
if (!wc.debugger.isAttached()) {
|
||
wc.debugger.attach('1.3') // 抛错则上抛,渲染端回退
|
||
}
|
||
attached.add(wc)
|
||
wc.once('destroyed', () => attached.delete(wc))
|
||
}
|
||
|
||
async function dispatchClick(wc, x, y) {
|
||
const base = { x, y, button: 'left', buttons: 1, clickCount: 1 }
|
||
await wc.debugger.sendCommand('Input.dispatchMouseEvent', { type: 'mouseMoved', x, y })
|
||
await wc.debugger.sendCommand('Input.dispatchMouseEvent', Object.assign({ type: 'mousePressed' }, base))
|
||
await wc.debugger.sendCommand('Input.dispatchMouseEvent', Object.assign({ type: 'mouseReleased' }, base))
|
||
}
|
||
|
||
async function handle(event, args) {
|
||
const wc = event.sender
|
||
const op = args && args.op
|
||
ensureAttached(wc)
|
||
switch (op) {
|
||
case 'insertText':
|
||
// 覆盖当前选区(渲染端已 selectAll)
|
||
await wc.debugger.sendCommand('Input.insertText', { text: String(args.text == null ? '' : args.text) })
|
||
return { ok: true }
|
||
case 'click':
|
||
await dispatchClick(wc, Math.round(args.x), Math.round(args.y))
|
||
return { ok: true }
|
||
case 'key': {
|
||
const key = String(args.key || '')
|
||
await wc.debugger.sendCommand('Input.dispatchKeyEvent', { type: 'keyDown', key })
|
||
await wc.debugger.sendCommand('Input.dispatchKeyEvent', { type: 'keyUp', key })
|
||
return { ok: true }
|
||
}
|
||
default:
|
||
throw new Error('unknown_op: ' + op)
|
||
}
|
||
}
|
||
|
||
function registerPageInput() {
|
||
ipcMain.handle('pageInput', handle)
|
||
}
|
||
|
||
module.exports = { registerPageInput }
|