從寫程式碼,到創作下一幕

探索 字節跳動 - 火山方舟 的 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)。標註將使用者選擇的內容 和回饋帶入對話;網站工具讓智能體能夠透過 應用程式的現有功能,基於這些上下文執行操作。