从写代码,到创作下一幕

探索 字节跳动 - 火山方舟 的 AI 编程与视频创作活动。

Agent Plan & Coding Plan

一站体验多款热门模型,为 AI 编程与智能体开发提供更多选择。新用户可联系(微信: goo_lvyouyou)免费体验 9.9 agent plan。

Seedance 2.5

让创意,跃然成片。探索 30 秒视频、多模态参考与局部编辑,把脑海中的画面变成下一支作品。

中文

标注扩展

浏览器标注 API 让你的网站能够自定义用户选择的内容、 随反馈附带的上下文,以及用户在向 ChatGPT 发送标注前 用于预览更改的控件。

浏览器标注无需更改任何代码即可在你的网站上使用。 用户可以选择页面的一部分、添加评论,并将其作为上下文发送给 Codex 或 ChatGPT Work。

作为开发者,你可以使用浏览器标注 API 提供应用专属的上下文或控件。例如, 你可以在设计系统预览中附加组件变体的预览,帮助开发者了解如何更新网站上的组件。

如需帮助理解浏览器标注 API 或为网站添加标注支持,请安装 Annotations Extensibility 插件。

安装 Annotations Extensibility 插件

试一试

在本指南中体验浏览器标注 API。

  1. 在 ChatGPT 的内置浏览器中打开此页面。
  2. 尝试打开此卡片中将出现的建议提示词。
  3. 进入标注模式,然后选择下方的表格或代码示例,在预定义的布局和主题之间切换。
  4. 你仍然可以为页面上的任意项目添加标注,并查看默认的标注行为。

在 ChatGPT 内置浏览器中打开此页以体验标注。

在 ChatGPT 的浏览器中打开

选择要自定义的内容

从适合你的网站的集成方式开始:

目标 集成方式
将卡片或其他一组元素作为一个对象来选择 选择目标
让用户选择短语或句子 文本选择容器
为所选内容附加更多上下文 选择元数据
通过你自己的按钮打开标注,并附上建议评论 标注请求
从你自己的 UI 请求对特定文本段落的反馈 文本范围请求
在标注打开时显示高级控件 编辑器默认设置
预览应用属性或收集选项 自定义控件
选择画布中绘制的单个对象 标注区域
从你的网站开启或关闭标注模式 标注模式控件

本指南适用于 ChatGPT 桌面应用的 DevDay 2026 版本及后续版本。 在应用的内置浏览器中,JavaScript API 通过 document.oai.annotation 提供, 适用于 HTTPS 或 localhost 等安全的顶层页面。启用后, 浏览器会在页面脚本运行前安装该 API。请对每个方法进行功能检测, 以支持较旧或不受支持的浏览器,然后在 DOM 元素存在后 初始化集成。无需就绪事件或轮询。

API 方法同步返回,注册句柄可立即 使用。浏览器可以在之后完成标注编辑器的加载。 标注区域的 hitTest 回调可以返回 promise。

自定义选择目标

默认情况下,标注模式从页面的 DOM 中选择元素,优先选择 文本、图像和控件等目标。要让较大的对象可被选择, 请用 oai-annotation-container 标记其所在区域,并用 oai-annotatable 标记 其中可选择的后代元素。

此示例让图表卡片可以作为一个对象被选择:

<section oai-annotation-container>
  <article id="chart-card" oai-annotatable="Q3 Revenue">
    <h2>Q3 Revenue</h2>
    <p>$120,000 this quarter</p>
    <button type="button">More info</button>
  </article>
</section>

将指针移到卡片内的任意位置都会高亮显示整张卡片。可选的 oai-annotatable 值为对象提供一个向用户和模型显示的名称。 请选择能区分相邻对象的名称,或省略该值。

容器定义这些选择规则的适用范围。单独添加 oai-annotatable 属性不会改变选择行为。 在容器内,浏览器会选择包含指针下元素的最近的已标记目标。 嵌套容器使用最近的容器。 容器内未标记的区域会创建网页标注;所有容器之外的区域 保持默认行为。

启用文本选择

为区域添加 oai-annotation-container-text,让用户可以在标注模式下 拖动选择文本。该属性无需设置值:

<article oai-annotation-container-text>
  <h2>Design guidelines</h2>
  <p>Use consistent spacing between related components.</p>
  <p>Leave more space between separate groups.</p>
</article>

完成非空选择并松开后,会打开文本标注编辑器,其中包含 所选范围及其上下文。仅点击而不选择文本不会创建 标注。按 Esc 键(Escape) 或取消手势会取消选择。

最近的文本选择容器或 DOM 选择容器决定手势的起始行为。 文本容器不使用 oai-annotatable 标记来拾取元素。嵌套 一个 oai-annotation-container 可恢复元素拾取,嵌套文本容器则可 恢复文本选择。如果同一元素同时具有这两个属性,文本选择 优先。

文本选择遵循页面的常规选择规则,可以超出 起始容器。容器不会裁剪所选范围,也不会启用 iframe 内的选择。文本字段仍可选择,但在标注模式下,普通页面点击和 控件的原生操作仍会被阻止。显式的 request() 调用保持现有行为。

为所选内容添加上下文

为已标记的目标添加 oai-annotation-metadata,以包含页面上可能 不可见的上下文。例如,一个虚构的联系人行可以包含 电子邮件地址,用于请求起草邮件:

<section oai-annotation-container>
  <div
    id="contact-row"
    oai-annotatable="Alex Morgan"
    oai-annotation-metadata='{"email":"alex@example.com"}'
  >
    <div>Alex Morgan</div>
    <div>Project lead</div>
  </div>
</section>

选择其中任意一行都会选中整个联系人行。元数据会与 标注一同显示,并随标注进入对话。仅包含你 打算同时与用户和模型共享的上下文。发送邮件仍然 需要连接邮件工具。

使用小型、扁平的 JSON 对象,并遵守以下限制:

  • 最多六个属性,值可以是字符串、有限数值、布尔值或 null。
  • 键最多 64 个字符,字符串值最多 256 个字符。
  • 序列化后的对象最多 2,048 字节。

键必须以 ASCII 字母开头,且只能包含 ASCII 字母、数字、 空格、下划线或连字符。词与词之间使用单个空格。不支持嵌套 对象和数组。浏览器会忽略无效的元数据。

你也可以通过 request() 或标注区域的 hitTest 结果提供元数据。

从你的网站打开标注

直接在用户交互中调用 document.oai.annotation.request(target, options), 例如按下按钮时。目标可以是当前文档可见区域内 已连接到文档的 HTML 元素,也可以是 DOM Range。目标无需具有标注 属性。不支持虚拟对象 ID。

网站发起的请求可能需要用户许可;用户可以在 网站工具 > 标注功能 下重新启用已被阻止的标注功能。

将此按钮添加到选择示例中的图表卡片旁,然后在 两个元素都存在后运行脚本:

<button id="discuss-chart" type="button" hidden>Explain more</button>
const card = document.getElementById("chart-card");
const button = document.getElementById("discuss-chart");

button.hidden = typeof document.oai?.annotation?.request !== "function";

button.addEventListener("click", () => {
  const annotation = document.oai?.annotation;
  if (typeof annotation?.request !== "function") return;

  annotation.request(card, {
    initialComment: "Explain the latest trend in this graph.",
  });
});

选择进一步解释 会请求浏览器打开标注,其中已选中卡片, 并包含可编辑的评论。用户可以编辑、保存标注,并随 消息发送。打开标注不会向 ChatGPT 发送消息;只有 用户可以提交。

请在当前用户交互期间完成调用。如果先等待网络请求, 可能会丢失该交互状态。

可选的第二个参数支持以下字段:

选项 行为
mode 使用 "advanced" 打开元素的高级控件,或使用 "default" 采用默认编辑器行为。默认为 "default"。使用 "default" 仍可打开自定义控件;请参阅编辑器默认设置。文本范围仅支持 "default"。
enterAnnotationMode 设置为 true 可进入标注模式,并在取消或发送标注后保持该模式。
metadata 有效的非空元数据会在此次请求中替换目标的 HTML 元数据。对于文本范围,以及目标元素位于 shadow DOM 内的情况,此选项会被忽略。
initialComment 提供可编辑的评论,最多 240 个 UTF-16 代码单元。

request() 返回一个包含 accepted 布尔值的对象。请检查 result.accepted,而非结果对象,以确定浏览器是否已接收 并验证请求。它不能确认编辑器是否已打开,也不能确认 用户是否已保存或发送标注。

编辑器加载期间,浏览器可以保留一个已接受的请求。在 有请求待处理、编辑器已打开或 ChatGPT 正在 控制浏览器时,浏览器可能拒绝另一个请求。

请求为文本范围添加标注

传入 DOM Range,可在不改变浏览器文本选择的情况下请求对某段文本的反馈。 此示例选择段落的内容;无需 oai-annotation-container-text 属性:

<p id="draft-passage">Leave more space between separate groups.</p>
<button id="discuss-passage" type="button" hidden>Discuss this passage</button>

在两个元素都存在后运行此脚本:

const passage = document.getElementById("draft-passage");
const button = document.getElementById("discuss-passage");

button.hidden = typeof document.oai?.annotation?.request !== "function";

button.addEventListener("click", () => {
  const annotation = document.oai?.annotation;
  if (typeof annotation?.request !== "function") return;

  const range = document.createRange();
  range.selectNodeContents(passage);
  annotation.request(range, {
    initialComment: "Suggest a clearer version of this guidance.",
  });
});

该范围必须包含当前文档中非空的可见文本,且 所选内容至少有一部分位于页面可见区域内。最多可包含 20,000 个 UTF-16 代码单元。不支持折叠范围、仅含空白字符的文本、所选文本 被隐藏的情况,以及封闭 shadow root 内的范围。

文本范围请求仅使用默认文本编辑器。包含 mode: "advanced" 的请求会被拒绝,请求元数据也会被忽略。已接受的请求 等待编辑器期间,请保持目标文本可用:浏览器会 在打开编辑器前再次检查该范围。文本选择容器则独立 支持用户在标注模式下拖动选择文本。

选择编辑器的默认模式

要让通过浏览器选择 UI 打开的标注立即显示 高级控件,请将此标签添加到页面的 <head> 中:

<meta name="oai-annotation-editor-default-mode" content="advanced" />

对于从你自己的 UI 打开的标注,请向 request() 传入 { mode: "advanced" }。使用 mode: "default" 或省略该参数,即可采用默认编辑器行为。 页面的 meta 设置不会覆盖此请求选项。默认模式 并不保证编辑器仅提供评论功能:具有建议初始值的自定义 控件,或省略 currentValue 的控件,都可能打开控件编辑器。

手动调整、折叠和 按住 Option 点击(Option-click) 控件仅在 Codex 或 localhost 上可用。在 ChatGPT 中的托管网站上,已注册的自定义控件 会自动显示,不提供调整 或折叠按钮。页面请求的 高级模式在这些网站上仍然有效。

添加自定义控件

使用 registerControls() 将标注控件关联到一个或多个 DOM 元素。控件可以预览应用属性,例如间距设计变量, 也可以收集要附带在请求中的选项,例如邮件语气。

预览共享的间距设计变量

此示例中的两张卡片使用相同的 CSS 属性:

<style>
  #component-preview {
    --card-padding: 16px;
  }

  .preview-card {
    padding: var(--card-padding);
    border: 1px solid #d1d5db;
  }
</style>

<section id="component-preview" oai-annotation-container>
  <article class="preview-card" oai-annotatable="Profile card">
    Profile card
  </article>
  <article class="preview-card" oai-annotatable="Summary card">
    Summary card
  </article>
</section>

创建预览后运行此脚本:

const preview = document.getElementById("component-preview");
const annotation = document.oai?.annotation;

function previewSpacing(event) {
  const { callback, value } = event.detail;
  if (callback === "setCardPadding") {
    preview.style.setProperty("--card-padding", `${value}px`);
  }
}

let registration;
if (typeof annotation?.registerControls === "function") {
  preview.addEventListener("oaiannotationcontrolchange", previewSpacing);
  registration = annotation.registerControls({
    targets: preview.querySelectorAll(".preview-card"),
    controlsHeading: "Card spacing",
    controlsMode: "replace",
    controls: [
      {
        type: "range",
        label: "Card padding (pixels)",
        callback: "setCardPadding",
        reference: "--card-padding",
        min: 8,
        max: 32,
        step: 4,
        currentValue: 16,
      },
    ],
  });
}

function disposeAnnotationControls() {
  preview.style.removeProperty("--card-padding");
  registration?.dispose();
  preview.removeEventListener("oaiannotationcontrolchange", previewSpacing);
}

为任一卡片添加标注,并将卡片内边距(像素) 从 16 改为 24。在 ChatGPT 中的托管网站上,控件会自动显示;在 Codex 或 localhost 上,按需选择调整。两张卡片都会更新。标注会记录标签、引用, 以及旧值和新值。清除预览会恢复原来的内边距。 移除组件时调用 disposeAnnotationControls()。

收集选项而不提供预览

使用元数据示例中的联系人行,提供邮件语气选项:

const contact = document.getElementById("contact-row");
const registration = document.oai?.annotation?.registerControls?.({
  targets: contact,
  controlsHeading: "Email options",
  controlsMode: "replace",
  controls: [
    {
      type: "select",
      label: "Email tone",
      callback: "emailTone",
      options: [
        { label: "Professional", value: "professional" },
        { label: "Friendly", value: "friendly" },
        { label: "Direct", value: "direct" },
      ],
      defaultValue: "professional",
    },
  ],
});

此控件无需事件处理程序,因为它不会预览页面 更改。省略 currentValue 会让浏览器包含所选语气, 即使用户保留初始选项也是如此。移除联系人行时 调用 registration?.dispose()。

对于选择控件,预览回调会接收 option.value,例如 "professional"。对于先前选项和当前所选选项,标注历史记录和 ChatGPT 都会收到可见的 option.label,例如 "Professional"。 请使用能说明各个选项的标签;value 中的内部 ID 不会 作为选项文本发送。

配置控件和初始值

使用 controlsMode: "replace" 可仅为已注册的目标显示你的控件, 或使用 "extend" 将其与内置控件一同显示。可选的 controlsHeading 用于为面板命名。浏览器会去除其首尾空白,并接受 1 到 80 个 字符。没有标题时,面板会显示元素的 HTML 标签。 标题不会包含在发送给 ChatGPT 的上下文中。

每个注册最多支持 12 个控件。每个控件都需要 type、可见的 label 和 callback 标识符:

类型 值 附加字段
color 十六进制颜色,例如 "#2563eb" 无
range 数值 min、max 和 step
select 字符串 options,一个由 { label, value } 对象组成的数组
toggle 布尔值 无

callback 是字符串标识符,而非 JavaScript 函数。确保它 在注册内唯一,以 ASCII 字母开头,并使用 ASCII 字母、 数字、下划线或连字符。可选的 reference 用于标识 正在更改的属性,并随标签和值一同包含在标注中。

将 currentValue 设置为属性的有效常规值,包括尚未保存的 编辑。不要将预览状态或未完成的输入用作基准。浏览器 使用它进行更改前后对比和重置。应用状态变化时,使用 registration.update({ controls }) 刷新控件的 currentValue 值。更新会影响之后的标注;现有标注保留其 已捕获的值。

设置 defaultValue 可提供建议初始值;在确定控件的初始 状态时,它优先于 currentValue。 如果两者都省略,颜色控件以白色开始,范围控件以最小值开始, 选择控件以第一个选项开始,开关控件则以 false 开始。

将建议值与常规草稿分开。如果更新抛出异常,或 请求失败或返回 accepted: false,请丢弃此次尝试的建议,并 恢复先前的控件和建议状态。请求被接受后,请保留建议: 浏览器可能会先将请求加入队列,再捕获控件。

验证控件和注册

浏览器会根据以下限制验证注册和更新:

字段或资源 约束
控件 每个注册最多 12 个,且 callback 标识符必须唯一。仅使用为该控件类型定义的字段。
标签和标题 控件标签、选项标签和 controlsHeading 去除首尾空白后必须非空,且最多 80 个 UTF-16 代码单元。控件文本不能包含控制字符或改变文本方向的字符。
标识符 callback 去除首尾空白后最多 80 个 UTF-16 代码单元,使用上述格式。reference 长度为 1–80 个字符,允许 ASCII 字母、数字和 _ . / : @ $ # -,不允许空格。
选择选项 1–12 个选项,每个选项的 value 字符串必须不同,且最多 512 个 UTF-16 代码单元。提供的 currentValue 和 defaultValue 必须与某个选项的值匹配。
范围值 min、max、currentValue 和 defaultValue 必须是 −10,000 到 10,000 之间的有限数值。必须满足 min < max,step 必须介于 0.001 到 10,000 之间且不大于 max - min,初始值必须在范围内。初始值无需与 step 对齐。
颜色和开关 颜色必须在 # 后使用 3、4、6 或 8 位十六进制数字。开关值必须为 true 或 false。
目标 注册时包含 1–128 个目标条目,全部为当前文档中的元素。更新时可传入 targets: [] 以解除关联。每个文档最多支持 64 个控件注册和 1,024 个注册与目标之间的关联。
序列化控件 包含 controls、controlsHeading 和 controlsMode 的 JSON 载荷必须在 16,384 个 UTF-16 代码单元以内。使用可编码为 JSON 的数据,不得包含函数、符号或大整数。

对于无效的定义、目标或超出限制的情况,registerControls() 和 registration.update() 可以 同步抛出异常。在 dispose() 之后进行更新 也会抛出异常。被拒绝的更新会保留先前的注册,包括 其控件和目标。请在调用处处理失败,保持常规 编辑功能可用,并复用或释放你管理的注册,以免超出 限制。

处理预览和重置

oaiannotationcontrolchange 事件从所选元素开始冒泡。其 detail 包含 callback、value 和 action:

操作 应用所提供的值以
preview 显示所请求的更改。
preview-original 临时显示原始状态以供比较。
reset 在清除预览时恢复原始状态。

与间距示例一样,对每种操作都应用所提供的值。确保 处理程序可逆,且可安全地重复调用。预览事件不会请求 永久更改;请通过应用的常规保存流程保存。将 常规草稿、未完成的输入和预览分开,避免无关的编辑 覆盖预览。当用户编辑同一设置时,替换其 预览,并更新基准值以供之后的标注使用。

保留注册句柄以便更新和清理。update() 接受 targets、controls、controlsHeading 和 controlsMode 的任意组合。 省略的字段保留先前的值。例如,替换组件的 DOM 元素时使用 registration.update({ targets: newElement })。 目标集合捕获现有元素,不会跟踪之后 匹配选择器的元素。

传入 targets: [] 可解除注册关联,传入 controls: [] 可清除其 控件。移除集成时,请调用 dispose() 并移除事件监听器。 清理还必须恢复常规渲染:dispose() 仅 移除控件注册,不会撤销你的预览更改。间距示例 通过移除内联覆盖来恢复原始 CSS 值。这两个方法 都同步返回,且不返回值。更新会影响 之后的选择;已保存的标注保留其捕获的控件和目标。

控件事件以标注捕获的元素为目标。更新 注册的目标不会重定向现有标注。重置事件 仍可能在已移除的元素上触发,因此其原父元素上的监听器不会 收到这些事件。

让画布对象可被选择

标注区域让应用能够识别画布内绘制的 单个对象。使用 registerSurface() 注册宿主元素,并提供 一个 hitTest 函数,返回带有稳定 id 的对象,或在 空白区域返回 null。

宿主必须是安全顶层文档中已连接到文档的 HTML 元素,且位于 shadow DOM 之外。不支持在 iframe 内注册标注区域。

此示例绘制一个收入柱形,并使其可被选择。将脚本放在 画布之后:

<canvas id="revenue-canvas" width="480" height="240">
  Revenue this quarter: $120,000.
</canvas>
const canvas = document.getElementById("revenue-canvas");
const context = canvas.getContext("2d");
const bar = { x: 40, y: 60, width: 320, height: 100 };

function drawRevenue(highlighted = false) {
  context.clearRect(0, 0, canvas.width, canvas.height);
  context.fillStyle = "#2563eb";
  context.fillRect(bar.x, bar.y, bar.width, bar.height);
  if (highlighted) {
    context.strokeStyle = "#111827";
    context.lineWidth = 3;
    context.strokeRect(bar.x, bar.y, bar.width, bar.height);
  }
}

drawRevenue();

const surface = document.oai?.annotation?.registerSurface?.({
  element: canvas,
  hitTest({ clientX, clientY }) {
    const bounds = canvas.getBoundingClientRect();
    const scaleX = bounds.width / canvas.width;
    const scaleY = bounds.height / canvas.height;
    const rect = {
      x: bounds.left + bar.x * scaleX,
      y: bounds.top + bar.y * scaleY,
      width: bar.width * scaleX,
      height: bar.height * scaleY,
    };

    if (
      clientX < rect.x ||
      clientX > rect.x + rect.width ||
      clientY < rect.y ||
      clientY > rect.y + rect.height
    ) {
      return null;
    }

    return {
      id: "revenue-this-quarter",
      name: "Revenue this quarter",
      role: "chart-bar",
      metadata: { Metric: "Revenue", Value: 120000 },
      rect,
    };
  },
  renderSelection({ hoveredId, selectedId }) {
    drawRevenue(
      hoveredId === "revenue-this-quarter" ||
        selectedId === "revenue-this-quarter"
    );
  },
});

在标注模式下将指针悬停在柱形上会高亮显示它。选择它会打开 标注,其中包含对象名称、元数据和所选内容的截图。

保持同一标注区域内的 ID 稳定。可选的 name 对用户可见; role 提供简短的语义描述。可选的 rect 使用 相对于页面可见区域的 CSS 像素,与 clientX 和 clientY 一致。请从场景坐标转换, 并考虑缩放比例、平移和缩放操作。

hitTest 可以返回 promise,并通过 signal 接收 AbortSignal,以 取消已被替代的工作。浏览器会等待 250 毫秒,然后 回退到 DOM 选择。发生错误或结果无效时也会回退。命中测试 成功但没有对象时,请显式返回 null。

被取消的工作仍必须使其 promise 兑现或拒绝。浏览器 同一时间只允许执行一个 hitTest 回调,并会一直占用该名额,直到 promise 完成,即使已经取消或超时也是如此。如果由工作进程处理拾取, 请在其工作被中止时使待处理的 promise 完成;丢弃已取消的工作进程 响应可能阻塞后续的画布拾取。

使用可选的 renderSelection 回调提供应用专属的反馈。 两个 ID 都为 null 时清除反馈。移动对象或更改缩放后 调用 surface?.invalidate(),移除集成时 调用 surface?.dispose()。两者都同步返回,且不返回值。

为画布对象添加控件

在标注区域的 DOM 元素上注册自定义控件。绘制的对象在 API 中 称为虚拟对象,它们使用你的自定义控件;内置 CSS 和文本 控件不适用于这些对象。

对于多个对象,请在 selectedId 变化时,使用 renderSelection 更新宿主的控件, 并在浏览器捕获标注前完成。控件 事件包含 detail.virtualTarget: { surfaceId, targetId }。请使用捕获的身份标识及其 callback 来路由每个事件,而非使用当前 所选对象。targetId 与 hitTest 中的 id 匹配;surfaceId 标识 浏览器的标注区域注册。普通 DOM 事件不包含 virtualTarget。

选择另一个对象后,预览、比较和重置事件仍保留原始对象的身份 标识。重新打开已保存的标注不会重新运行 命中测试来替换其捕获的对象或元数据。

控制标注模式

使用 toggle() 请求模式更改,使用 isActive() 读取已确认的状态, 并通过文档的 oaiannotationmodechange 事件保持 UI 同步。 请对这两个方法进行功能检测,以支持较旧或不受支持的浏览器。添加此按钮, 然后在按钮存在后运行脚本:

<button id="toggle-annotations" type="button" hidden>
  Enter annotation mode
</button>
const button = document.getElementById("toggle-annotations");
const annotation = document.oai?.annotation;

function renderMode(active) {
  button.textContent = active
    ? "Exit annotation mode"
    : "Enter annotation mode";
}

const onModeChange = (event) => renderMode(event.detail.active);
const onClick = () => annotation.toggle(!annotation.isActive());

if (
  typeof annotation?.toggle === "function" &&
  typeof annotation?.isActive === "function"
) {
  document.addEventListener("oaiannotationmodechange", onModeChange);
  button.addEventListener("click", onClick);
  renderMode(annotation.isActive());
  button.hidden = false;
}

function cleanupAnnotationButton() {
  document.removeEventListener("oaiannotationmodechange", onModeChange);
  button.removeEventListener("click", onClick);
  button.hidden = true;
}

toggle() 会切换模式。传入 true 可确保模式开启,传入 false 可确保 模式关闭。使用相同布尔值重复请求具有幂等性。传入 true 会保留已打开的编辑器或待完成的激活;false 会取消 待完成的激活,并采用常规退出流程。

在正在进行的用户交互中调用 toggle()。其同步返回的 { accepted } 结果表示已收到请求,并不确认模式已更改。浏览器的 资格检查仍可能阻止更改。与示例一样,从 isActive() 和事件中读取状态。

isActive() 无需用户手势。浏览器会在派发 oaiannotationmodechange 前更新它,后者的 event.detail.active 是布尔值。浏览器 不会发送初始事件,也不会为强制执行但无实际变化的操作发送重复事件,因此请通过读取方法(getter) 初始化 UI。如果在模式启用期间访问权限被撤销,最后一个事件会报告 active: false,保留的读取方法 则返回 false。

标注模式会捕获页面点击。按住 空格键(Space) 可使用页面控件, 包括你的退出按钮,也可以通过浏览器的标注 UI 退出。 仅按住 空格键(Space) 时,isActive() 仍为 true。API 不支持 oai-annotation-ignore 或其他允许点击穿透的属性。

退出会关闭编辑器并保留已保存的标注,不会提交 标注,也不会将其移入消息输入框。移除组件时 调用 cleanupAnnotationButton()。捕获的 API 引用让清理逻辑能够移除 监听器,即使命名空间已经消失。

测试集成

在桌面应用的内置浏览器中打开你的网站,并测试 你添加的功能:

  1. 进入标注模式,选择对象和文本。检查高亮、 名称、范围和元数据是否与预期目标一致。
  2. 从网站的按钮打开标注。检查所选元素或 文本范围、初始评论和编辑器模式。
  3. 更改自定义控件,与原始状态比较,然后清除预览。 检查应用是否恢复原始状态。
  4. 对于画布内容,测试空白区域、尺寸调整和场景变化。切换 对象,确认控件事件仍会更新捕获的目标。取消 异步命中测试,并确认之后的拾取仍然有效。
  5. 从消息输入框的附件预览中保存、重新打开并编辑标注。 检查标注是否保留其控件和目标,以及移除标注是否会清除 所有预览。
  6. 随消息发送标注。确认 ChatGPT 收到 所选内容、元数据和请求的值,包括可见的选择 标签,以及省略 currentValue 的控件中未更改的选项。
  7. 在没有该 API 的浏览器中打开网站,验证常规 交互仍然有效。

要提供 ChatGPT 可在你的网站上执行的操作,请添加 网站工具(WebMCP)。标注将用户选择的内容 和反馈带入对话;网站工具让智能体能够通过 应用的现有功能,基于这些上下文执行操作。