Agent 控制提示层
agent 操作 macOS 原生窗口时,OpenProgram 在该窗口上方画两样东西:一个说明 agent 正在控制的状态胶囊,以及停在上一动作处的 agent 指针。agent 操作网页时显示同一个指针。本文规定两者的外观与动效、使用的颜色,以及保证它们不妨碍用户的规则。
预览
同一时刻的聊天应用:agent 刚点击了输入框。左边是之前的渲染,右边是现在的渲染。用按钮播放点击、输入和结束状态。
之前
现在
强调色 #5b5bd6,是占位值(见强调色)。其余部分都是中性色。
绘制位置
| 目标 | 画什么 | 代码位置 |
|---|---|---|
macOS 原生窗口(computer_use 窗口动作) | 状态胶囊和 agent 指针,画在一个无边框、不激活、点击穿透的 NSPanel 里;每个受控窗口由一个 helper 进程负责。 | gui_harness/adapters/mac_indicator.py,由 mac_window.py 的 _WindowControlIndicator 启动。OpenProgram 在固定的 Harness 提交之上通过 program-patches/gui_harness.patch 交付它。 |
| 桌面 App 中的网页标签页 | agent 指针,画在网页上方一个透明、不可聚焦、点击穿透的窗口里。 | apps/desktop/main/action-cues.js 负责摆放窗口;menu-overlay/action-cue 负责渲染。 |
| 对话预览(PiP)和 Web 版 | agent 指针,画在预览图上方一个 pointer-events: none 的图层里。 | browser-control-bar.tsx 中的 ActionCueTravel。 |
三处 Web 界面使用同一个组件 AgentCursor。网页的控制状态(Active、Pausing…、Paused)仍显示在网页工具栏和浮动控制条上,见 GUI Agent。网页不再另加状态胶囊。
状态胶囊
- 位置。受控窗口的顶部居中,距窗口上边缘 6pt;大多数应用在这里只有标题或空白标题栏。它随窗口移动,并裁剪在窗口范围内。
- 形状。高 26pt,全圆角。宽度随文字变化,最多为窗口宽度减 24pt;文字过长时以省略号截断。
- 表面。中性深色玻璃:
rgba(28,28,33,.86),0.5pt、10% 白色描边,8pt 柔和阴影。在浅色和深色应用上都清晰可读。 - 文字。系统首选语言为中文时显示“OpenProgram 正在操作 WeChat”,否则显示 “OpenProgram is using WeChat”。文字只写应用名,不显示窗口 id 或进程 id。结束状态显示“OpenProgram 已结束操作 WeChat”,最多 4 秒。
- 活动圆点。7pt 强调色圆点,agent 控制期间以 0.9 秒为周期在完全不透明和 35% 之间闪烁。结束状态下圆点变灰并停止闪烁。
Agent 指针
- 图形。经典箭头,高约 22pt,比系统指针稍大,避免与之混淆。强调色填充,1.5pt 白色描边,2.5pt 投影。周围没有圆环。
- 热点。箭头尖端位于 42pt(原生)或 40px(Web)方框的中心,方框以动作点为中心摆放。原生动作的动作点是控件在动作前的边界中心。
- 标签。箭头右下方的小强调色标签写明动作:点击、选择、输入或滚动(Click、Select、Type、Scroll)。它不包含输入的文字。网页指针不带标签,由网页工具栏的操作历史写明动作。
- 停靠。第一次动作前,原生指针以 60% 不透明度停在窗口中央,让人在任何操作发生前就看出窗口归 agent 控制。
- 移动。到达新的动作点时,按距离在 0.18–0.42 秒内以 ease-in-out 曲线滑过去。网页指针保留原有的 80–280 毫秒 ease-out 移动。窗口在移动途中变化时,滑动会改向新的位置。
- 点击反馈。到达按压或选择的位置后,一个直径 32pt 的圆环在 0.45 秒内从 35% 放大到 135% 并淡出。输入和滚动没有波纹。
- 恢复。同一窗口的后续步骤在原位恢复上一轮指针,不重播移动和波纹。
- 减弱动态效果。系统要求减弱动态效果时,指针直接跳到位,关闭波纹和圆点闪烁。
强调色
产品正在放弃之前的橙色与陶土色强调色,改用自己的配色,围绕深靛蓝、紫罗兰和青绿色,这套配色另行设计。因此提示层先使用冷色的靛紫 #5b5bd6 作为占位。这个值只出现在两处,两处必须保持一致:
- apps/web/app/styles/base.css 中
:root的--agent-accent,所有主题共用。 mac_indicator.py中的ACCENT,以 sRGB 分量表示。
只有指针填充、波纹、标签和活动圆点使用强调色。胶囊、文字和描边都是中性色,所以换成最终品牌强调色时不会牵动其他部分。
安全规则
- 不接收输入。原生面板设置
ignoresMouseEvents且不激活。网页指针窗口忽略鼠标且不能获得焦点。预览图层是pointer-events: none。它们都不移动系统指针。 - 控制期间始终可见。helper 在整个窗口会话期间运行,目标被其他窗口遮住时仍绘制胶囊。目标最小化或关闭时隐藏。失败、取消或窗口身份丢失时立即关闭。只有正常结束的步骤会显示结束状态。
- 每个窗口一个所有者。每个受控窗口有自己的 helper 和面板。两个会话控制两个窗口时,各自显示自己的胶囊和指针,提示层之间不会互相遮挡。
- 只负责显示。绘制失败记为警告,不改变动作结果,也不回退为前台输入。
与 GUI Agent 设计的关系
GUI Agent 设计规定了本文遵循的规则:提示层不激活应用、忽略鼠标,跟随窗口,在遮挡下仍可见;点击显示短暂的圆环;提示属于目标,不移动用户的指针;减弱动态效果时使用静态标记。之前的渲染遵守了这些规则,但外观不符合:
| 方面 | 之前的渲染 | 现在 |
|---|---|---|
| 位置 | 窗口左上角,压在窗口自己的控件上。 | 窗口顶部居中,位于标题栏区域。 |
| 文字 | “控制中 · WeChat · 34327”:只有中文,并带窗口 id,看起来像进程 id。 | 按系统语言显示“OpenProgram 正在操作 WeChat”,不显示 id。 |
| 尺寸 | 固定 224 × 28pt,长名称被截断。 | 高 26pt,宽度随文字。 |
| 颜色 | 徽标和标记都用饱和紫色填充。 | 中性深色玻璃;只有指针和圆点用强调色。 |
| 指针 | 42pt 紫色圆环,里面是随动作变化的白色 SF Symbol。 | 强调色箭头,尖端落在动作点上,另加小动作标签。 |
| 动效 | 标记在位置之间直接跳动。 | 缓动滑动;第一次动作前指针停靠在中央。 |
| 点击反馈 | 没有;圆环一直存在。 | 按压或选择后播放一次短波纹。 |
| 停止 | 提示层上没有入口。 | 提示层上仍然没有入口,这是有意的设计(见下)。 |
本文与所回应需求的不同之处
- 放在窗口顶部,而不是屏幕顶部。每个受控窗口拥有自己的提示层,两个会话可能同时控制两个窗口。固定在屏幕顶部的胶囊会和其他会话的胶囊叠在一起,可能落在与窗口不同的显示器上,也看不出受控的是哪个窗口。窗口顶部居中既保持胶囊在顶部,又让它与目标绑定。
- 原生窗口周围不加发光边框。GUI Agent 设计把状态边缘和边框限定在 OpenProgram 自己的界面上(网页标签页和对话预览,它们已有状态边缘),不在其他应用的窗口上画边框。胶囊和指针已足以表明控制状态。
- 原生胶囊上没有停止按钮。胶囊必须忽略鼠标。在那里放可点击区域,会盖在受控应用上,拦截本该给应用的点击;helper 也没有经过验证的通道回到运行本身。停止仍在 OpenProgram 中进行:对话里的停止控件和 Running 面板。网页浮动控制条保留自己的暂停控件,因为它画在 OpenProgram 自己的网页视图上。
实现状态
原生 helper(通过 Harness 源码补丁)和三处 Web 界面均已按本文实现。原生面板已用真实 AppKit 画在一个桌面窗口上并从屏幕截取:胶囊、箭头、标签和结束状态都按规定绘制。尚未在安装版 App 驱动微信等真实应用时验收。波纹和滑动只通过运动规则的单元测试(Harness 中的 tests/test_indicator_presentation.py)检查过,没有在屏幕上观察。