跳转至

Browser Annotation API

标注扩展让用户从网页选中对象、文字或画布区域,补充意见与参数提议,再随消息带入对话。它补充WebMCP动作,不负责替用户发送消息或执行模型修改。官方来源见参考资料。

支持条件

官方指南面向 DevDay 2026 发布及之后的桌面应用,入口为 document.oai.annotation。在启用时,API于页面脚本执行前注入安全顶层文档,例如HTTPS或localhost。DOM元素存在后初始化,逐方法功能检测,不需要轮询或等待自造的ready事件。

企业与教育工作区还受 Site annotation customizations 策略控制。普通网页标注不要求网站改代码;定制能力受站点权限和方法支持约束。画布surface不支持iframe或shadow DOM中的host,不能从WebMCP的frame支持推导标注也支持。

接口总表

接口 用途 结果与边界
oai-annotation-container / oai-annotatable 将DOM区域作为可选对象 单独放 oai-annotatable 不改变选择规则
oai-annotation-container-text 标注模式中拖选文字 与元素容器同处时文字选择优先
oai-annotation-metadata 携带少量业务定位 用户与模型都能看到,不是隐藏数据
request(target, options) 从用户点击打开标注请求 同步 {accepted},不证明打开、保存或发送
registerControls(options) 注册颜色、范围、枚举、开关 返回可 update() / dispose() 的句柄
registerSurface(options) 画布内稳定对象命中与反馈 返回可 invalidate() / dispose() 的句柄
toggle(active?) 请求进入、退出或切换模式 同步 {accepted},从实时用户交互调用
isActive() 读取已确认模式 不需要用户手势
oaiannotationcontrolchange 预览、原始对比和重置 可逆临时呈现,不是永久保存
oaiannotationmodechange 已确认模式变化 detail.active;无初始事件或强制无变化的重复事件

注册方法同步返回,hitTest可以异步。无方法、异常和拒绝都不得破坏产品原有编辑器。

选区与定位元数据

<section oai-annotation-container>
  <figure
    id="review-image"
    oai-annotatable="当前候选图片"
    oai-annotation-metadata='{"resource":"image-42","revision":"7","region":"coat"}'
  >
    <img src="/assets/candidate.webp" alt="当前候选图片" />
  </figure>
</section>

DOM元数据是小型扁平JSON:最多6个属性;值为字符串、有限数、布尔或null;key最长64字符,字符串值最长256字符,序列化最多2048字节。key以ASCII字母开头,只用字母、数字、单空格、下划线或连字符,不支持嵌套对象和数组。无效元数据会被忽略。

图片区域用原始图坐标或归一化坐标保存到业务文件;元数据带精确来源与区域引用。屏幕像素用于当前高亮,不能作为长期区域事实。完整坐标与意见可从获权文件或工具读取,不将大文档塞进元数据。

从产品按钮打开标注

以下片段放在已存在的 review-image、discuss-image按钮与annotation-status状态元素之后。回退可以继续使用应用原有的意见文件或复制入口。

const target = document.getElementById("review-image");
const button = document.getElementById("discuss-image");
const status = document.getElementById("annotation-status");

button.hidden = typeof document.oai?.annotation?.request !== "function";
button.addEventListener("click", () => {
  const annotation = document.oai?.annotation;
  if (typeof annotation?.request !== "function") return;
  try {
    const result = annotation.request(target, {
      initialComment: "仅调整已标区域,其余内容保持不变。",
      metadata: { resource: "image-42", revision: "7", region: "coat" },
    });
    status.textContent = result.accepted
      ? "已请求打开标注"
      : "未接受请求,请使用页面意见入口";
  } catch {
    status.textContent = "无法打开标注,请使用页面意见入口";
  }
});

request()必须直接位于当前用户交互中;先等待网络保存可能丢失手势。需要先保存时,分成“保存”与之后的显式打开动作,不制造“已发送”回执。

options字段 行为
initialComment 可编辑意见,最多240个UTF-16 code units
mode default 或 advanced;文字Range仅支持default
enterAnnotationMode 进入模式,取消或发送后保持该模式
omitScreenshot 请求省略自动截图,默认false
metadata 对普通元素,以有效非空对象替代HTML元数据;文字Range和shadow DOM内部元素忽略

目标可以是当前文档中已连接、位于可见区域的HTML元素或有效DOM Range,不能直接传虚拟画布对象ID。文字Range需非空、非纯空白、可见且至少部分在视口,最多20000个UTF-16 code units。调用后保持目标可用,宿主打开编辑器前还会重验。

保存、输入框与发送的区别

sequenceDiagram
  actor U as 用户
  participant W as 网站
  participant H as 标注编辑器
  participant C as 会话
  participant B as 业务工具
  U->>W: 选区并点击讨论
  W->>H: request 目标、定位和初始意见
  H-->>W: accepted 或拒绝
  H-->>U: 按可用性打开编辑器
  U->>H: 修改、预览并保存意见
  U->>C: 随消息提交选定意见
  C->>B: 根据意图读取原图、修订及区域
  B-->>C: 当前授权与来源核验
  C->>B: 在授权范围内继续原修改任务
  B-->>C: 原操作与真实结果

浏览器API没有提供网站可依赖的“用户已发送”回调。业务可记录自己的保存状态,但不能从 accepted、退出模式或关闭弹层推断用户提交。退出模式会保留已保存标注,不会自动提交或移动到输入框。

普通网站不读取或改写宿主输入框。MCP App可协商消息和上下文桥接,自有客户端可使用App Server;这两条路径各有自己的授权和发送行为,见原生界面与运行时。

可逆的自定义控件

registerControls()接收 targets、controlsHeading、controlsMode 和 controls。controlsMode: "replace"可替换内置控件;各类型必须使用明确属性名,例如“线宽”,不能随当前值把标签变成“细线”。

类型 值 附加字段
color 3、4、6或8位十六进制颜色 无
range 有限数 min、max、step
select 字符串 options: [{label, value}]
toggle 布尔 无

每个控件有 label 和唯一的字符串 callback,callback不是函数。reference可指向业务属性或token。currentValue表示普通草稿的真实基线,defaultValue是建议起始值并优先于它;基线不能取自临时预览或未完成输入。

const annotation = document.oai?.annotation;
let registration;
if (typeof annotation?.registerControls === "function") {
  registration = annotation.registerControls({
    targets: target,
    controlsHeading: "区域修改",
    controlsMode: "replace",
    controls: [{
      type: "select",
      label: "修改范围",
      callback: "editScope",
      options: [
        { label: "仅已标区域", value: "region" },
        { label: "整张图片", value: "image" },
      ],
      defaultValue: "region",
    }],
  });
}
// 卸载组件时调用;有预览的集成还需恢复普通呈现和移除监听。
function cleanupControls() {
  registration?.dispose();
}

此例仅收集选择,没有直接预览或业务保存。省略 currentValue 时,即使用户保留初始选择也会把选项带入标注;select发送给模型的是可见label,不是内部value。重要定位ID另外通过元数据或业务引用保存。

oaiannotationcontrolchange.detail携带callback、value、action。preview、preview-original和reset都应用事件给出的值,处理器需可重复、可恢复。普通草稿、建议值和预览分开保存;永久保存继续走产品原流程。

关键限制:每注册最多12控件、1到128个目标;label、heading、callback最长80个UTF-16 code units;select为1到12选项;文档最多64注册和1024个目标关联;控件序列化上限16384个UTF-16 code units。range有限值在-10000到10000之间,min < max,step在0.001到10000且不大于范围。注册和更新可同步抛错,拒绝更新保留旧注册。

registration.update()只影响未来标注。已有标注保持捕获目标和基线;DOM替换后不会把旧事件重定向到新节点。dispose()不自动回滚应用的临时预览,移除旧节点后也可能收到对旧目标的reset,应保留正确清理路径。

画布对象

registerSurface({element, hitTest, renderSelection?})用于canvas等非DOM对象。hitTest接收clientX、clientY和signal,返回稳定id或明确的null;还可返回name、role、metadata和rect。

rect是可见视口的CSS像素坐标,需要转换场景缩放和平移。ID在surface内稳定,不能随排序和缩放变化。renderSelection收到hoveredId和selectedId,用来绘制应用自己的高亮。

hitTest应读取本地索引,不发模型或业务网络。宿主允许250ms后降级DOM选择;错误与无效结果也降级。异步命中一次只允许一个在途槽位,取消后promise仍需resolve或reject,不能丢弃worker回复导致后续永远无法选择。

缩放、移动与对象变化后调用surface.invalidate(),卸载时surface.dispose()。虚拟对象自定义控件事件包含detail.virtualTarget: {surfaceId, targetId},必须路由到捕获对象,不能使用之后的新选区替代它。

模式、隐私与降级

模式按钮在用户点击中调用toggle(),通过isActive()和oaiannotationmodechange显示真实状态。相同布尔值请求幂等;accepted不证明状态改变。标注模式会捕获页面点击,按住Space可临时操作页面;不存在可依赖的oai-annotation-ignore穿透属性。

只有确需更深入控件才设置mode: "advanced"或页面meta默认。用户取消、权限撤销、API缺失、宿主占用与旧版本都应回到普通编辑器。截图和元数据仅含当前任务获准内容;省略截图不意味着元数据没有隐私风险。

验收须覆盖保存后重开、清除预览、切换对象、移除DOM、模式撤权、异步取消和无API浏览器。详见验证矩阵。