用 peekaboo + System Events 可靠地操控 macOS 原生应用与手机 App(搜索、点击、播放、调音量、点外卖、点咖啡、下单、支付等)。核心是「归一化窗口几何 + 优先键盘/菜单、坐标只兜底、点前自检」。覆盖:网易云音乐等桌面软件;以及经 iPhone 镜像操控 iOS App——点外卖/点咖啡/美团外卖下单付款(profile: meituan-waimai-ios-mirror)、微信收发消息(profile: wechat-ios-mirror)等防截屏/反自动化的手机 App。当任务是操控某个本地 App 或手机 App 界面(点外卖、点咖啡、美团、微信、各种桌面/手机软件)时【必须先调用本技能并读对应 profile,按归一化+grab.sh截图+cliclick 那套来,别直接抓 peekaboo MCP 自由发挥、别用 peekaboo see/image(本机 bridge error)】。不适用于浏览器内的事(走 Playwright MCP,且别去开美团网页版 waimai.meituan.com——要登录、付不了款)。
Scanned 9/6/2026
Install to Claude Code
npx -y skills add XiaoChu-1208/inner-coordinates --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of screen-pilot?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/xiaochu-1208-screen-pilot)More formats (shields.io, HTML) on the badges page.
---
name: screen-pilot
description: 用 peekaboo + System Events 可靠地操控 macOS 原生应用与手机 App(搜索、点击、播放、调音量、点外卖、点咖啡、下单、支付等)。核心是「归一化窗口几何 + 优先键盘/菜单、坐标只兜底、点前自检」。覆盖:网易云音乐等桌面软件;以及经 iPhone 镜像操控 iOS App——点外卖/点咖啡/美团外卖下单付款(profile: meituan-waimai-ios-mirror)、微信收发消息(profile: wechat-ios-mirror)等防截屏/反自动化的手机 App。当任务是操控某个本地 App 或手机 App 界面(点外卖、点咖啡、美团、微信、各种桌面/手机软件)时【必须先调用本技能并读对应 profile,按归一化+grab.sh截图+cliclick 那套来,别直接抓 peekaboo MCP 自由发挥、别用 peekaboo see/image(本机 bridge error)】。不适用于浏览器内的事(走 Playwright MCP,且别去开美团网页版 waimai.meituan.com——要登录、付不了款)。
---
# screen-pilot — 桌面界面操控
目标:少截图、不靠会漂移的绝对坐标、不点错按钮。把一个 App「学一次」,之后照 profile 精准复用。
我们其实在替这些不暴露可访问性树的 App **从外面反向重建一张交互地图**:第一次睁着眼学(慢),之后照这张「内心的坐标」图走(快)。
坐标是消耗品会过期,真正的资产是这套**会学、会验、会自愈**的回路。库(`profiles/`)越用越大。
> **术语:内心链路(inner chain)。** 指「学好之后,凭已记/已算的坐标,胸有成竹地一口气把一串动作执行完,中途不截图」。
> 本文里凡说「跑内心链路 / 直点 / 直发 / 直做 / 闭眼快做」都是这个意思——**不是"盲"、不是冒险,是照内心那张图走**。只在卡点(算不出的内容相关步、或链路断了)才睁眼截图。
## 渠道选择(step 0:先判断,再让用户定)
本 skill 有**两条渠道**,动手前先定走哪条:
- **(A) 直接操控 Mac 原生窗口**(如网易云音乐、各种桌面软件)。更快、无镜像延迟。
- **(B) 经 iPhone 镜像操控 iOS App**(如微信、美团外卖)。动作发生在手机上。
判断规则:
- 该 App **有能用的 Mac 版、内容能 `screencapture`、不反自动化** → 走 **A**。
- Mac 版**防截屏/反自动化**(微信),或**只有 iOS 版/小程序**(美团),或**用户账号数据在手机上** → 走 **B**。
- **两条都可行时**:给出判断+推荐,然后**一句话让用户拍板**走哪条(如「这个我能用 Mac 版,也能用你手机镜像,走哪个?」)。别替用户默选,也别为这个反复纠结——问一句即可。
## 生命周期(每次要操控某个 App 都走这条)
1. **认出**:查 `profiles/_index.json` 有没有这个 App。
2. **没学过** → 问用户「要不要花 ~30s 学一次?学完以后就秒级」。同意 → 进「睁眼学」。不同意 → 当场临时操作不入库。
3. **学过** → 跑 `lib/normalize.sh <进程> [W H]` 拿指纹:
- **几何对齐**(窗口被挪/拉也被它弹回固定矩形,缓存坐标复活)。
- **核版本**:和 profile 的 `learnedVersion` 一致 → 放心闭眼飞快做;**不一致 = 可能改版** → 点前更要自检,错了就把那个元素重学、更新 profile。
4. **闭眼快做(exploit)**:normalize → 优先快捷键 → 菜单 → 缓存坐标(相对偏移+窗口position) → **每个动作后验证**。
5. **自愈**:任一验证失败 = 地图过期 → 退回「睁眼学」补那一处 → 写回 profile + 更新 `_index.json` 的 version。
「睁眼学」= 截图定位元素 → 记成**相对窗口原点的偏移** + 记下达成它的最稳方式(快捷键/菜单/坐标) → 写进该 App 的 profile。
**「内心 figma」原则(顺手记全屏)**:你为了点一个键,已经截图看清了整屏。那就别只记那一个键——**把这一屏所有可见功能的位置都顺手记进 profile**,包括这次没点的(标题、tab、分类项、其它按钮、列表结构)。代价几乎为零(图已在眼前),收益是下次到同一屏不必再裁图找,脑内直接定位任意功能。
- 边界:记你**确实路过的每一屏**的全部可见键;但**别为没用上的功能专门去翻新页面**(增量长,按需访问新屏)。两者不矛盾:访问到的屏记满,不刻意扩张访问范围。
- 坐标随滚动/内容浮动的(列表项、分类项),记下来同时标注「随滚动变,用前截图重定位」;不动的骨架(搜索框、tab、底栏、键盘)记成稳定坐标。
- **从录制/截图学时,逐帧仔细看,别扫缩略图把看不清的当「噪声」跳过。** 每一屏都是一个 state、每个跳转都是一条边,全画进图——没有噪声帧。(2026-06-21 实测教训:我把 montage 一扫,把「付款成功后怎么回首页」那几帧当噪声跳过、漏画了那条边,被用户抓到。)
**学完就落脚本(最重要的产物)。** 光在 profile 里记坐标 ≠ 能一次跑完——那等于把「重组链路 + 逐个点」留给模型,它一组装就触发「做一步看一眼」、一张张截图。**真正的可执行产物是脚本**:一段学好的稳定链路,落成一条 `lib/<app>-<flow>.sh`(含所有点击+等待,中文用 `pbcopy` 设剪贴板再 cliclick 真实 Cmd+V),参数化(店名/商品/规格…)。agent 跑这**一条命令**就走完整段,物理上没有「中间」可截,逐步观察的反射被绕开。
- **秘密边界:脚本跑到密码/付款这一步必须【停】,密码绝不写进脚本。** 脚本只含公开的【路径】(点击坐标+等待);到支付密码这种只有用户能给的秘密,脚本停下,由 agent 问用户、把口令**实时映射到密码键盘坐标逐位点入**——键位在 profile(哪个数字在哪),**数字本身不进任何脚本/文件**。脚本=可共享的路径,密码=用完即弃的实时口令。
- **错了修脚本,不重学。** 某个坐标漂了 → 改脚本里那一个数;这是脚本级自愈,比重跑一遍学习快得多。
- profile 仍要(记屏图/坑/哪稳哪变),但它是**给脚本当说明书和坐标来源**;脚本才是跑起来的东西。
- 见 `lib/meituan-order.sh`(参数化全链路)、`lib/meituan-search.sh`(搜索段)为范例。
## 链路置信度(confidence-gated execution)—— 截不截图由数据决定,不由模型临场决定
**默认翻转:学过/演示过的链路 = 高置信(trusted),下次就该代码级一次性通过、零截图、不唤起大模型逐步判断。** 不是"先攒够次数证明给我看才信"——信任是**默认给的**(你都学过/演示过了),出错了才收回。模型每介入一步就手痒想截图;把"截不截图"这个决策从模型手里拿走、交给链路的**历史一次性成功率**这个硬数据,多余截图就从机制上消失。
每条链路(一个 flow,对应一条 `lib/<app>-<flow>.sh` 脚本)带一个 `confidence` 状态:
- **`trusted`(默认,学过/演示过即是)** → **跑脚本,代码级一条命令通过**。零截图、不逐步唤起模型判断。开局那张定位截图也尽量省掉——脚本第一步无条件复位到锚点(如退回列表页/首页)就不必看"现在停在哪"。只认脚本退出码:`exit 0` = 成功,非 0(如 `FOCUS_LOST`)才回头。
- **`observing`(仅在出错后降级到此)** → 才回到"开局定位一张 + 内心链路 + 卡点睁眼"的模式,睁眼补那一处、修脚本里那个坐标,跑通一次(一次性、无需中途修正)→ **自动升回 `trusted`**。
- **`oneShotSuccesses` 计数器**:累计"一次性成功(中途无需睁眼/无需修正)"的次数。它**不是准入门槛**,是健康度记录:连续成功累加、信心越来越足;任一次失败 → `confidence: observing`、计数清零、记 `lastFailure`。
- **演示/录制是最强信任来源**:用户亲自录过的链路(如 wechat-flowmap 的发消息路径),直接 `trusted`、`demonstratedBy: user`,第一次就走脚本,别再睁眼学一遍。
**判据一句话:这条链路有成品脚本且 `confidence: trusted` → 直接 `bash lib/<app>-<flow>.sh 参数...`,看退出码,不截图。** 没脚本/`observing` 才睁眼。内容相关的一步(读对方发来什么、列表第几行有红点)天然算不出,仍要截一张看——那不算"链路截图",是输入采集。
> 范例:`lib/wechat-send.sh <联系人> <消息>`(发消息已演示,trusted)、`lib/meituan-order.sh`(下单全链路)。`profiles/_index.json` 每条 `learned` 记 `confidence`/`oneShotSuccesses`/`demonstratedBy`。
## 两条铁律(实测踩过的坑,必须遵守)
1. **任何键盘输入前,先 `activate` 目标 App。** 坐标点击(peekaboo click)不管焦点都落在目标窗口上,
但**键盘事件(⌘A / ⌘V / 回车 / type)只发给当前最前台 App**。不先聚焦,你的文字会打进终端/别处,
搜索框纹丝不动还以为成功了。每段键盘操作前都补一句:
`osascript -e 'tell application "PROC" to activate'`(sleep 0.4)。
2. **每一步动作后给页面渲染时间,再做下一步;关键步点前先自检。** 搜索回车后结果要 ~2s 才渲染好,
太快点「单曲」标签会点在没加载完的页面上、落空。搜索→等 2s→切 tab→等→双击行→验证。
**页面跳转/加载要等更久**(实测:搜索结果 ~2s;进店/结算/收银台等大跳转 ~2.5-3.5s;支付结果页 ~3.5-4s)。
宁可慢半拍:**跳转类操作后多等一会儿,再截图确认目标已出现**,没出现就再等、别赌时机、别点空。
(用户明确反馈:这类流程跳转/等待很常见,等待纪律比手快更重要。)
## 自动执行原则(用户要自动化时,默认这样)
用户明确叫你「自动/帮我做完/一路做到X」时,他要的是**自动化结果,不是被反复追问**。默认姿势:
- **最快 = 一次性推理整条链路 + 只在卡点截图学习。** 开始前可以(也最好)跟用户对齐一句「我要做什么、到哪一步停」;一旦跑起来,**只在真正的卡点才停**(算不出的内容相关步、或链路断了)。
- **别中途反复问。** 能用已记/已算的坐标做的,绝不停下来问、绝不再学一遍。链路一旦确认,就**默认一口气直达目标**,多往前走几步,别一步一确认(那是胆小,不是稳)。
- **隐私/安全的停与不停**:只有当用户**可能忽略了**某个隐私或安全后果时才停下提醒(如「这会真扣款/把隐私发出去,你确定?」);如果用户**已经明确要这个结果**,就别问——他知道自己在干嘛。花钱/不可逆的临门一脚仍可截一张图自检(看,不是问)。
- 唯一例外是支付密码这类**必须由用户给的秘密**:到那一步问用户要,不存、不猜。
## 定位一次 → 一口气跑内心链路(localize-then-batch,profile 已存在时的正确姿势)
**核心执行模型:每一次都「进行到底」。** 不是一步一停、一张张截图——那是第一次睁眼学的节奏。学过一遍、profile/脚本在手后,模型的默认行为应当是:
- **固定节点理想只有一个:开局定位**——看镜像状态 + App 停在哪屏。这是唯一算不出来的(App 停在上次离开的地方)。定位完、复位到锚点(如首页),**之后整条链路全是常量/可算的,一路跑到底、不再截图**。
- **每次尝试都一路推理内心链路跑到底**,中间不回头逐个确认。
- **某步还会漂 = 它还没被换成常量、或脚本还没修好**,不是"该多截一张图"的理由。**复盘修掉它**:从当前屏反推「断在哪一步、为什么」→ 把那步换成常量 / 改脚本里那个坐标 → **从断点一次性再跑到底**。绝不退回「逐步点」。
具体怎么走:
学过一遍、profile 在手后,**别再「点一下截一下」**——那是第一次睁眼学的节奏,慢且没必要。换成:
1. **入口定位(截一张)**:App 常停在上次离开的那屏(不会复位)。进来先截**一张**,对照 profile 认出「现在停在哪屏」。
2. **复位到锚点 或 就地接链路**:要么先导航回规范起点(如美团点底部「首页」tab、或连点返回退到已知屏),从锚点起跑最稳;要么从当前屏直接接。
3. **跑内心链路一串**:链路里**稳定骨架**的坐标(搜索框、tab、底栏、固定按钮、密码键盘、规格弹窗里位置固定的选项)直接按 profile 坐标**连续点,中途不截图**。
### state 感知(内心坐标自觉)—— 你随时知道自己停在哪个节点,走最短边,别无脑复位
**你不是每次都从零开始的截图机器人,你是已经接入这个 App 的程序:心里时刻有一个「当前 state」变量。** 你刚做完什么动作、现在停在 flowmap 的哪个节点,你是**知道**的(不用截图问自己)——做完一个动作就在心里把当前 state 推进到下一个节点。要执行下一件事时:**从当前 state 在 flowmap 上找到目标节点,走最短的那几条边**,而不是条件反射式地先退回锚点再导航过去。
- **复位到锚点是有成本的**(退两次返回 + 重新搜索 + 等待渲染),只在「当前 state 真的不确定」或「就地路径反而更绕/更脆」时才值得。**已经站在目标节点上,就地把事做完**——别先回主菜单再绕回来。
- 典型反面教训(2026-06-24 小刍抓到):我已经在「对方会话页」读完消息,要回他,却退回列表→重新搜索→重新进会话才发——等于一个明知自己在哪的程序却每次回主菜单重新导航。正解:**就地接链路**(`wechat-send.sh ... here` 跳过导航直接发,发完留在会话页继续盯)。
- 脚本要支持「起点参数」反映这一点:同一条发送链路,`from=here`(已在会话页就地发)和 `from=list`(从列表搜索进入)是两条不同长度的路径;agent 按自己**当前 state** 选对应入口,而不是只有「从锚点起跑」一种。
- **回复就地,监测回首页看角标(2026-06-24 小刍定)。** 两件事用两种 state:①**回复**——在目标会话页就用 `here` 就地发(别退回去重搜,那是无脑复位);②**监测对方有没有新消息**——回首页(列表页),靠 App 自己的**未读红角标**判断,角标是 App 给的现成「有无新消息」信号,比每轮进会话猜省。流程:回首页等角标 → 某行角标更新 → 进那个会话(`here` 读+回)→ 回完退回首页继续等角标。别守在会话页空转,也别每轮无脑进会话。
- **★核未读角标必须用 `lib/grab-badges.sh`,别看 grab.sh 全图(2026-06-24 血泪)。** 微信未读角标 = 联系人**头像右上角的小红点**(不是行右侧,那是时间戳!)。这红点只有几像素,grab.sh 截全窗口后整图传给模型会被降采样**压糊、红点糊没** → 连漏好几次误判"没新消息",小刍发高清图才发现。`grab-badges.sh` 只截**左侧头像那一窄列 + 放大 2x**,小区域保留原始像素密度,哪行头像有红点一眼可辨。通用教训:**核任何几像素的小细节(角标/小图标/勾选态)都别看整图缩略——裁那一小块放大单独 Read。**
- **冷热双模式(2026-06-24 小刍定,对话热时提速到 ~15s)。** `ScheduleWakeup` 下限是 60s,对话冷时够用;但对方聊得快时 60s 太慢。两挡自适应:
- **冷模式(默认)**:回首页,每 60s `ScheduleWakeup` + `grab-badges.sh` 核角标。省电、适合零星消息。
- **热模式(对方连发、聊得快时切入)**:**守在会话页别退首页**,`run_in_background` 跑 `lib/wechat-watch.sh 15 20`——每 15s 截会话消息区比对 md5,对方一发新消息→画面变→脚本退出→harness 当场唤醒我→看图直接 `here` 回→再续一个 watch。延迟 ~15s,远快于 60s。
- **切换**:watch 报 `IDLE`(连续 20 轮≈5 分钟没动静)= 对方安静 → 退回首页转冷模式;冷模式发现对方又开始连发 → 切回热模式。
- **基线坑**:watch 第一帧作 md5 基线,所以**发完自己的回复后再启动 watch**,基线含自己消息、之后变化才是对方的(否则自己的回复会触发一次空唤醒)。动图/语音波形会持续触发,看一眼无新内容续 watch 即可。
- **监视区只盯下半屏气泡**:watch 截图区从 `y+300` 起(避开顶部「对方正在输入…」提示——它一闪一闪会误触发),到输入框上方。新消息总滚到底部,下半屏一定抓得到。
- **解读焊进监视器,主 agent 只在真消息时被唤醒并拿到现成文字(2026-06-24 小刍想法,已落地)。** 别让主 agent 自己 Read 会话截图(图进主上下文,聊几十轮就爆),也别每轮被空触发唤醒。`wechat-watch.sh` 升级版自带解读:md5 测到画面变 → 脚本内部调 **headless `claude -p`** 解读那帧 → **解读=NONE(撤回/正在输入/自己消息渲染等空触发)就自己消化、继续盯、不唤醒主 agent**;解读出对方新消息才 `echo "NEW_MSG: …"` 退出唤醒主 agent。主 agent 被唤醒时 task 输出里就是「对方说了X」,直接 `here` 回,全程不碰图。
- **headless 解读命令(安全且实测可行)**:`claude -p "<读图prompt>" --allowedTools Read --model sonnet --output-format text`。**只白名单只读 Read、绝不加 `--dangerously-skip-permissions`**(那会造无门禁自主 agent,被 auto-mode 正确拦截)。
- **模型别用 haiku**:实测 haiku 读中文微信截图会读错气泡原文(连「有无新消息」之外的内容都读错),拿它解读直接回会答非所问 → sonnet 起步。
- **代价**:每次画面变化调一次 sonnet 解读(~14s、~15k tokens),但全在 background;过滤掉空触发后主 agent 只在真消息时醒一次,省主 agent 回合 + 主上下文不堆图。md5 没变就不解读,省调用。
- **并行终极版:常驻 daemon + 哨兵(2026-06-24 小刍洞察「sonnet 盯截图,你盯 sonnet」,最优形态)。** 单进程 `wechat-watch.sh` 的病根是**串行**:它 exit 唤醒主 agent 后,到主 agent 处理完重启它之间,sonnet 是停的、空档漏消息。终极版拆成两个常驻 background,让 sonnet 和主 agent 真并行:
- **`wechat-watch-daemon.sh`(常驻,sonnet 盯截图)**:一直跑不退,每 15s 截图比 md5、变化就 sonnet 解读,对方新消息 **append 到收件箱 `/tmp/wechat_inbox.txt`**,自己继续盯。主 agent 处理/打字时它不停。
- **`inbox-wait.sh`(哨兵,主 agent 盯 sonnet)**:盯收件箱,一有新行就打印新消息并 exit → 唤醒主 agent。
- **主 agent 被唤醒后的固定动作**:读哨兵输出的对方新消息 → `here` 回复 → **`touch /tmp/wechat_reply_marker`**(通知 daemon 基准前移、防把旧消息重复入箱)→ **只重启 `inbox-wait.sh`,daemon 绝不重启**。
- **去重靠 marker**:daemon 解读「我方最后一条之后对方的全部新消息」,按尾部增量入箱;主 agent 一回复就 touch marker,daemon 把基准移到我方新气泡之后,旧消息不再算新。
- **开关时机**:热对话开 daemon(`run_in_background` 跑 `wechat-watch-daemon.sh 15 240 12`,第三参=连续冷多少轮报 COLD);用户说撤 → `TaskStop` 掉 daemon + 哨兵(它常驻烧 sonnet token)。
- **冷了退主界面扫全局、切活跃会话(2026-06-25 小刍定的混合模式闭环)。** 别死守一个冷掉的会话、错过别处找你的人。两个唤醒源各有处理:
- **哨兵退出(`NEW_FROM_THEM:`)= 当前会话有新消息** → 读文字 → `here` 回 → touch marker → 重启哨兵(daemon 不动)。
- **daemon 退出(`COLD:`)= 当前会话连续 ~3 分钟冷** → ① `TaskStop` 掉哨兵(它还盯着旧 inbox)② 退回主界面(列表页,连点返回)③ `grab-badges.sh` 扫红角标看哪个会话有新消息 ④ 有 → 进那个会话 → 重挂 daemon + 哨兵(切到新活跃会话);都没有 → 退到 60s `grab-badges` 冷轮询,有人冒头再切回热模式开 daemon。
- 本质:**守会话(并行快) 与 盯列表(全局) 在单界面下不可兼得**,用「冷却 3 分钟自动切换」在两者间跳——热聊享并行、冷了不漏别处。
- **NEW_MSG 是自己的就别回**:解读偶尔仍可能把我方消息误判带出,主 agent 看到收件箱内容是自己刚说的话,直接跳过不回,别自己截图核对(小刍明确:不要自己读图)。
**核心判据(按置信度走):能算出来的链路就不截图;截图只在两种意外时跑——①操作疑似失误(点完该变没变、焦点可能丢了、价格/件数对不上预期)②碰到完全的新东西(profile 没记过的屏/弹窗/字段)。**
- 即:下一步状态**你能预测** → 直点、连发一批,不看;**不能预测或结果不符预期** → 截一张再说。
- 内容相关的坐标(列表第几行、滚动后的分类、随行变的「选规格」按钮)本质就是「算不出来」,归入「新东西/需确认」,看一眼。
- 唯一即便有把握也值得截一张的例外:**花钱/不可逆的临门一脚**(确认支付、提交订单、删项)——一张图的保险,便宜。
**原则:常量永远胜过变量。** 能把「内容相关、会漂移」的步骤换成「固定锚点」就一定换——如美团**店内用搜索框搜商品名**(搜索框坐标固定、结果第一条稳定),**别去赌随滚动浮动的分类/列表坐标**。该处的「大胆」= 走确定路径,不是盲点一个记忆里的位置;**盲点变量必漂**。(2026-06-21 实测教训:进店没走店内搜索、去盲点分类栏,分类落到「冰爽冷饮」、选规格点中「玫瑰香椰冷萃」,整条链漂掉。)
## 边界处理(实测)
- **窗口被拉伸/挪动 → 一句 set bounds 复位。** normalize 不只开局用,**每次复用缓存坐标前都先跑一遍**,
几何就回到指纹。注意有些 App 有最小窗口尺寸(网易云 ~1056x752),所以归一化后**回读实际几何**。
- **冷启动可能吞输入。** `open` 返回不等于能交互;先**轮询到窗口出现**(count windows ≥ 1),再小等一下。
但不要赌时机——**动作后一律验证**:搜索后确认结果渲染出来了,没有就说明还没就绪 → 等一下重发。
这条「验证+重试」自愈,不管机器快慢都稳。
- **快捷键优先于点坐标**(前提仍是先 activate):能用 ⌘→/空格 这种就别去点按钮。
## 终极绕法:iPhone 镜像驱动 iOS App(对付防截屏/反自动化的 App,如微信)
某些 App 在 Mac 上既防截屏(截到空白)又反自动化(会风控)。绕法:**在 iPhone 镜像窗口里操控手机上的那个 App**——
动作发生在 iOS(App 只看到一次正常触摸,不触发 Mac 风控),而镜像窗口是**普通 macOS 窗口**,`grab.sh` 能截到内容、`peekaboo` 能点。两道防线一起绕开,已对微信全自动跑通。要点:
- **前提**:iPhone 镜像配好;**接力/Handoff 必开**(Universal Clipboard 靠它把 Mac 剪贴板同步到 iOS,不开则镜像里粘贴是空的);`brew install cliclick`。
- **截图/点击**:`grab.sh "iPhone Mirroring"` 截图(手机屏可见)、`peekaboo click` 点 —— 跟普通窗口一样。
- **任何输入前先清空输入框。** 框里可能残留上次的字(搜过的词、历史),不清空就粘会变成「皮爷咖啡皮爷咖啡」。聚焦后先 `cliclick kd:cmd t:a ku:cmd`(Cmd+A 全选)→ `cliclick kp:delete`(删),再粘。空框也无害。
- **输入中文**:剪贴板 + **`cliclick kd:cmd t:v ku:cmd`**(真实 Cmd+V)。peekaboo/osascript 发 Cmd+V 会被镜像**把 Cmd 吃掉只剩 v** —— 必须 cliclick 这种真实 CGEvent 才带得动修饰键。中文用 `pbcopy` 设剪贴板(避开 osascript 引号坑)。
- **发送**:**`cliclick kp:return`(普通 Return)**。绝不用 Cmd+Return —— 那会把输入框顶进多行模式,Return 变换行发不出。
- **★代聊就必挂轮询(铁律):只要还在代someone聊天,发完当前回复就立刻挂轮询盯新消息——别发完一条就停手等用户来催。** 推荐形态是「**列表扫描 + 白名单 + 退避**」的自管循环(用定时器每轮唤醒自己):
- **列表扫描**:每轮回到微信会话列表页(不死守一个会话),`grab.sh` 截一张看哪行有红色未读角标,命中就点进去回、回完退回列表。坐标见 `wechat-flowmap.json` 列表页节点(会话行 y、返回键)。**点前必新截一张**——公众号推送会把列表行序顶乱,凭上一张的行号点会点错(实测点成被顶上来的公众号)。
- **行位/对象拿不准时,别赌行坐标——搜全名。** 点搜索框→粘联系人/群全名→**点第一个联系人结果(固定坐标)必命中**:微信极少重名,「最常使用/联系人」分区第一条就是本人。这是把「会漂的行坐标」换成「固定锚点(搜索框+第一结果)」,比数行点行稳得多(合「常量永远胜过变量」)。
- **白名单**:只回【用户配置的名单】(联系人/群名存本地、不入库),名单外有红点也只看不回。
- **退避节奏**:1 分钟一轮起;同档连续 3 轮没新消息就退一档(1m→5m→10m 封顶);任何时候有新消息→立刻打回 1 分钟(对话热就跟得紧)。
- **永不自动撤**:只有用户【明确】说撤才停;别因为「像收尾了/几轮没消息」就自己判断着撤,也别每轮催问要不要撤。
- 每轮截图**用之即弃**(读完 rm,不留盘)。状态(档位/空轮数/定时器 id/上轮列表快照)存本地一个 state 文件,跨轮不丢。
- 详见 `profiles/wechat-ios-mirror.json`。
## 决策顺序(从稳到不稳,能用上面就别用下面)
1. **键盘快捷键** —— 最稳,零坐标,布局变了也不怕。
2. **菜单按名字点**(System Events)—— 播放/暂停/音量/上一首下一首这类几乎都在菜单里。
菜单项的**名字本身还能当状态验证**(如「控制」菜单首项是「播放」=已暂停,是「暂停」=正在播放)。
3. **a11y 树**(`peekaboo see --app X`)—— 能读到就按元素 role/label 拿实时坐标,免缓存。
4. **缓存坐标**(本 profile)—— 只在上面都不行(a11y 读不到的烂 App)时兜底,**且只缓存不动的骨架**
(搜索框、标签、列表第 N 行的位置),**绝不缓存会变的内容**(具体某首歌、某条消息)。点之前先自检。
## 归一化窗口(坐标可复现的前提)
不要用绿灯/全屏(全屏会进独立 Space + 隐藏菜单栏,坑)。用 System Events 把窗口**摆到固定矩形**,再**回读实际几何**当指纹:
```bash
osascript -e 'tell application "System Events" to tell process "PROC"
set frontmost to true
set position of front window to {0, 25}
set size of front window to {1440, 900}
end tell'
# 回读(有些 App 有最大/最小限制,记实际生效的)
osascript -e 'tell application "System Events" to tell process "PROC" to get {position, size} of front window'
```
指纹 = `{bundleId, 进程名, 实际 position, 实际 size, 屏幕缩放}`。复用缓存前先核指纹,**任一不符就重学**(换分辨率/App 更新会让坐标整体偏,闭眼点很危险)。
## 缓存坐标怎么存怎么用
- **存相对窗口左上角的偏移**,不存绝对屏幕坐标:`dx = 元素x - 窗口x`,`dy = 元素y - 窗口y`。
- 用时读实时窗口 position,`点击点 = (窗口x + dx, 窗口y + dy)`。窗口被挪了也不错位。
## 学一个新元素坐标(贴左上角 + 只截窗口那块,别全屏+裁剪)
把窗口 normalize 到**贴左上角的固定矩形**(`set position {0,25}` → 回读实际 y,菜单栏高度被回读自动吸收,这台是 30)。
然后**只截窗口那块**,不全屏(小 3 倍、快、还省掉裁剪):
```bash
lib/grab.sh NeteaseMusic /tmp/w.png # = screencapture -x -R <窗口实际几何>
```
坐标换算变得极简:**截图像素 (px,py) ÷ 缩放(2) = 窗口相对坐标**(截图原点就是窗口原点,不用算全屏偏移)。
存进 profile 的就是这个相对偏移;用时 `绝对 = 窗口position + 偏移`。
> peekaboo 的 `image` 在本机 beta 版常报 bridge error,所以用系统 `screencapture -R`。
> 防截屏的 App(微信)用 grab.sh 照样空白 —— 那种靠**用户全屏截图**(用户自己截能看到内容),我按固定窗口几何反算坐标。
## 点前自检 + 点后验证
- 点列表项前:先截一张确认「第 1 行确实是我要的那个」(标题文字对不对),再双击。
- 动作后:用菜单项名 / 一张小截图确认状态真的变了(如播放→暂停)。不对就重来,别假设成功。
## 本机注意事项(实测)
- `peekaboo image` / `see` / `menu list` 在这台机的 3.0.0-beta3 上对部分 App 报
`PeekabooBridge ... error 1` —— 改用系统 `screencapture` 截图、用 System Events 读/点菜单。
- `peekaboo click --coords "x,y"`(加 `--double` 双击)工作正常,坐标是**逻辑点**(和 AppleScript 一致)。
- **中文输入走剪贴板**,别直接 type(避开输入法):
`osascript -e 'set the clipboard to "中文"'` 然后 `peekaboo hotkey --keys cmd,v`。
## profiles/
每个 App 一个 JSON,见 `profiles/neteasemusic.json`(已实测跑通:搜索→单曲 tab→双击第一首播放,
播放/暂停/音量全走「控制」菜单)。新 App 照它的结构学一份。
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!