標註擴充功能
瀏覽器標註 API 讓你的網站能夠自訂使用者選擇的內容、 隨回饋附帶的上下文,以及使用者在向 ChatGPT 傳送標註前 用於預覽更改的控制項。
瀏覽器標註無需更改任何程式碼即可在你的網站上使用。 使用者可以選擇頁面的一部分、新增評論,並將其作為上下文傳送給 Codex 或 ChatGPT Work。
作為開發者,你可以使用瀏覽器標註 API 提供應用程式專屬的上下文或控制項。例如, 你可以在設計系統預覽中附加元件變體的預覽,幫助開發者瞭解如何更新網站上的元件。
如需幫助理解瀏覽器標註 API 或為網站新增標註支援,請安裝 Annotations Extensibility 外掛。
安裝 Annotations Extensibility 外掛
試一試
在本指南中體驗瀏覽器標註 API。
- 在 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 引用讓清理邏輯能夠移除
監聽器,即使命名空間已經消失。
測試整合
在桌面應用程式的內建瀏覽器中開啟你的網站,並測試 你新增的功能:
- 進入標註模式,選擇物件和文字。檢查醒目、 名稱、範圍和後設資料是否與預期目標一致。
- 從網站的按鈕開啟標註。檢查所選元素或 文字範圍、初始評論和編輯器模式。
- 更改自訂控制項,與原始狀態比較,然後清除預覽。 檢查應用程式是否恢復原始狀態。
- 對於畫布內容,測試空白區域、尺寸調整和場景變化。切換 物件,確認控制項事件仍會更新捕獲的目標。取消 非同步命中測試,並確認之後的拾取仍然有效。
- 從訊息輸入框的附件預覽中儲存、重新開啟並編輯標註。 檢查標註是否保留其控制項和目標,以及移除標註是否會清除 所有預覽。
- 隨訊息傳送標註。確認 ChatGPT 收到
所選內容、後設資料和請求的值,包括可見的選擇
標籤,以及省略
currentValue的控制項中未更改的選項。 - 在沒有該 API 的瀏覽器中開啟網站,驗證常規 互動仍然有效。
要提供 ChatGPT 可在你的網站上執行的操作,請新增 網站工具(WebMCP)。標註將使用者選擇的內容 和回饋帶入對話;網站工具讓智能體能夠透過 應用程式的現有功能,基於這些上下文執行操作。