Ekstensibilitas Anotasi
Browser Annotation API memungkinkan situs web Anda menyesuaikan apa yang dipilih pengguna, konteks yang menyertai masukan mereka, dan kontrol yang mereka gunakan untuk mempratinjau perubahan sebelum mengirim anotasi ke ChatGPT.
Anotasi browser dapat digunakan di situs Anda tanpa perubahan kode. Pengguna dapat memilih bagian halaman, menambahkan komentar, dan mengirimkannya dalam Konteks ke Codex atau ChatGPT Work.
Sebagai pengembang, Anda dapat menggunakan Browser Annotation API untuk menyediakan konteks atau kontrol khusus aplikasi Anda. Sebagai contoh, Anda dapat melampirkan pratinjau varian komponen dalam pratinjau sistem desain agar pengembang mengetahui cara memperbarui komponen di situs web.
Untuk mendapatkan bantuan dalam memahami Browser Annotation API atau menambahkan dukungan anotasi ke situs web Anda, instal plugin Annotations Extensibility.
Instal plugin Annotations Extensibility
Coba
Lihat cara kerja Browser Annotation API dalam panduan ini.
- Buka halaman ini di browser bawaan ChatGPT.
- Coba buka saran prompt yang akan muncul di kartu ini.
- Masuk ke mode Anotasi, lalu pilih tabel di bawah atau contoh kode untuk beralih di antara tata letak dan tema yang telah ditentukan.
- Anda tetap dapat memberi anotasi pada item apa pun di halaman dan melihat perilaku anotasi default.
Buka halaman ini di browser bawaan ChatGPT untuk mencoba anotasi.
Pilih apa yang akan disesuaikan
Mulailah dengan integrasi yang sesuai dengan situs web Anda:
| Tujuan | Integrasi |
|---|---|
| Membuat kartu atau kelompok elemen lain dapat dipilih sebagai satu objek | Target pilihan |
| Memungkinkan pengguna memilih frasa atau kalimat | Kontainer pemilihan teks |
| Menyertakan konteks tambahan pada pilihan | Metadata pilihan |
| Membuka anotasi dari tombol Anda sendiri dengan saran komentar | Permintaan anotasi |
| Meminta masukan tentang bagian teks tertentu dari UI Anda sendiri | Permintaan rentang teks |
| Menampilkan kontrol lanjutan saat anotasi dibuka | Pengaturan default editor |
| Mempratinjau properti aplikasi atau mengumpulkan pilihan | Kontrol kustom |
| Memilih objek individual yang digambar di dalam kanvas | Permukaan anotasi |
| Mengaktifkan atau menonaktifkan mode Anotasi dari situs Anda | Kontrol mode Anotasi |
Panduan ini ditujukan untuk aplikasi desktop ChatGPT rilis DevDay 2026 dan yang lebih baru.
API JavaScript tersedia melalui document.oai.annotation di browser
bawaan aplikasi, pada halaman tingkat teratas yang aman seperti HTTPS atau localhost. Saat
diaktifkan, browser memasangnya sebelum skrip halaman Anda dijalankan. Deteksi ketersediaan
setiap metode untuk mendukung browser lama atau yang tidak didukung, lalu inisialisasi
integrasi Anda setelah elemen DOM-nya tersedia. Tidak diperlukan peristiwa kesiapan atau polling.
Metode API mengembalikan hasil secara sinkron, dan handle pendaftaran siap digunakan
segera. Browser dapat menyelesaikan pemuatan editor anotasi setelahnya.
Callback hitTest suatu permukaan dapat mengembalikan promise.
Sesuaikan target pilihan
Secara default, mode Anotasi memilih elemen dari DOM halaman, dengan mengutamakan
target seperti teks, gambar, dan kontrol. Agar objek yang lebih besar dapat dipilih,
tandai area yang memuatnya dengan oai-annotation-container dan elemen turunannya yang dapat dipilih
dengan oai-annotatable.
Contoh ini membuat kartu grafik dapat dipilih sebagai satu objek:
<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>Mengarahkan penunjuk ke mana pun di dalam kartu akan menyorot seluruh kartu. Nilai opsional
oai-annotatable memberi objek nama yang ditampilkan kepada pengguna dan model.
Pilih nama yang membedakan objek-objek yang berdekatan, atau hilangkan nilainya.
Kontainer menentukan tempat aturan pemilihan ini berlaku. Atribut
oai-annotatable saja tidak mengubah perilaku pemilihan.
Di dalam kontainer, browser memilih target bertanda terdekat yang memuat
elemen di bawah penunjuk. Kontainer bertingkat menggunakan kontainer terdekat.
Area tanpa tanda di dalam kontainer menghasilkan anotasi halaman web; area di luar
semua kontainer mempertahankan perilaku default.
Aktifkan pemilihan teks
Tambahkan oai-annotation-container-text ke suatu area agar pengguna dapat menyeret untuk memilih teks
selama mode Anotasi. Atribut ini tidak memerlukan nilai:
<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>Melepaskan pilihan yang tidak kosong akan membuka editor anotasi teks dengan rentang yang dipilih beserta konteksnya. Mengklik tanpa memilih teks tidak membuat anotasi. Escape atau gestur yang dibatalkan akan membatalkan pilihan.
Kontainer pemilihan teks atau DOM terdekat menentukan bagaimana gestur dimulai.
Kontainer teks tidak menggunakan penanda oai-annotatable untuk memilih elemen. Sarangkan
oai-annotation-container untuk memulihkan pemilihan elemen, atau kontainer teks untuk
memulihkan pemilihan teks. Jika kedua atribut ada pada satu elemen, pemilihan teks
diutamakan.
Pemilihan teks mengikuti aturan pemilihan normal halaman dan dapat meluas melampaui
kontainer awal. Kontainer tidak membatasi rentang atau mengaktifkan pemilihan
di dalam iframe. Kolom teks tetap dapat dipilih, tetapi klik halaman biasa dan
tindakan bawaan pada kontrol tetap diblokir selama mode Anotasi. Pemanggilan
request() secara eksplisit mempertahankan perilaku yang ada.
Tambahkan konteks ke pilihan
Tambahkan oai-annotation-metadata ke target bertanda untuk menyertakan konteks yang mungkin tidak
terlihat di halaman. Misalnya, baris kontak fiktif dapat menyertakan
alamat email untuk permintaan penyusunan draf email:
<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>Memilih salah satu baris teks akan memilih seluruh baris kontak. Metadata muncul bersama anotasi dan menyertainya dalam percakapan. Sertakan hanya konteks yang memang ingin Anda bagikan kepada pengguna dan model. Mengirim email tetap memerlukan alat email yang terhubung.
Gunakan objek JSON kecil dan datar dengan batasan berikut:
- Hingga enam properti, dengan nilai string, angka berhingga, boolean, atau
null. - Kunci hingga 64 karakter dan nilai string hingga 256 karakter.
- Hingga 2.048 byte untuk objek yang diserialisasi.
Kunci harus dimulai dengan huruf ASCII dan hanya berisi huruf ASCII, angka, spasi, garis bawah, atau tanda hubung. Gunakan satu spasi di antara kata. Objek bertingkat dan array tidak didukung. Browser mengabaikan metadata yang tidak valid.
Anda juga dapat menyediakan metadata melalui request() atau hasil hitTest
suatu permukaan.
Buka anotasi dari situs web Anda
Panggil document.oai.annotation.request(target, options) langsung dari interaksi
pengguna, seperti menekan tombol. Target dapat berupa elemen HTML yang terhubung
di dalam area dokumen saat ini yang terlihat atau
Range DOM. Target tidak memerlukan atribut
anotasi. ID objek virtual tidak didukung.
Permintaan yang dimulai situs mungkin memerlukan izin pengguna; pengguna dapat mengaktifkan kembali fitur anotasi yang diblokir melalui Alat situs > Fitur anotasi.
Tambahkan tombol ini di samping kartu grafik dari contoh pemilihan, lalu jalankan skrip setelah kedua elemen tersedia:
<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.",
});
});Memilih Jelaskan lebih lanjut meminta browser membuka anotasi dengan kartu terpilih dan komentar yang dapat diedit. Pengguna dapat mengedit, menyimpan, dan mengirimkannya bersama pesan mereka. Membuka anotasi tidak mengirim pesan ke ChatGPT; hanya pengguna yang dapat mengirimkannya.
Pastikan pemanggilan tetap berada dalam interaksi pengguna yang aktif. Menunggu permintaan jaringan terlebih dahulu dapat menyebabkan konteks interaksi tersebut hilang.
Argumen kedua yang opsional mendukung kolom berikut:
| Opsi | Perilaku |
|---|---|
mode |
Gunakan "advanced" untuk membuka kontrol lanjutan bagi suatu elemen, atau "default" untuk menggunakan perilaku editor default. Nilai defaultnya adalah "default". Kontrol kustom tetap dapat dibuka dengan "default"; lihat Pengaturan default editor. Rentang teks hanya mendukung "default". |
enterAnnotationMode |
Tetapkan ke true untuk masuk ke mode Anotasi dan tetap berada di dalamnya setelah membatalkan atau mengirim anotasi. |
metadata |
Metadata yang valid dan tidak kosong menggantikan metadata HTML target untuk permintaan ini. Opsi ini diabaikan untuk rentang teks dan ketika elemen target berada di dalam shadow DOM. |
initialComment |
Menyediakan komentar yang dapat diedit, hingga 240 unit kode UTF-16. |
request() mengembalikan objek dengan boolean accepted. Periksa
result.accepted, bukan objek hasilnya, untuk mengetahui apakah browser menerima
dan memvalidasi permintaan. Hasil ini tidak mengonfirmasi bahwa editor telah terbuka atau bahwa
pengguna telah menyimpan atau mengirim anotasi.
Saat editor dimuat, browser dapat menampung satu permintaan yang diterima. Browser dapat menolak permintaan lain saat ada permintaan yang menunggu, editor terbuka, atau ChatGPT sedang mengendalikan browser.
Minta anotasi untuk rentang teks
Teruskan Range DOM untuk meminta masukan tentang suatu bagian teks tanpa mengubah
pilihan teks browser. Contoh ini memilih isi paragraf; contoh ini tidak memerlukan
atribut 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>Jalankan skrip ini setelah kedua elemen tersedia:
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.",
});
});Rentang harus memuat teks terlihat yang tidak kosong dalam dokumen saat ini, dengan setidaknya sebagian pilihan berada di area halaman yang terlihat. Rentang dapat memuat paling banyak 20.000 unit kode UTF-16. Rentang yang diciutkan, teks yang hanya berisi spasi, teks terpilih yang tersembunyi, dan rentang dalam shadow root tertutup tidak didukung.
Permintaan rentang teks hanya menggunakan editor teks default. Permintaan dengan
mode: "advanced" ditolak, dan metadata permintaan diabaikan. Pastikan teks
target tetap tersedia saat permintaan yang diterima menunggu editor: browser
memeriksa rentang lagi sebelum membukanya. Secara terpisah, kontainer pemilihan teks
memungkinkan pengguna menyeret untuk memilih teks selama mode Anotasi.
Pilih mode default editor
Untuk langsung menampilkan kontrol lanjutan bagi anotasi yang dibuka melalui
UI pemilihan browser, tambahkan tag ini ke <head> halaman Anda:
<meta name="oai-annotation-editor-default-mode" content="advanced" />Untuk anotasi yang dibuka dari UI Anda sendiri, teruskan { mode: "advanced" } ke
request(). Gunakan mode: "default", atau hilangkan, untuk perilaku editor default.
Pengaturan meta halaman tidak menimpa opsi permintaan ini. Mode default
tidak menjamin editor yang hanya berisi komentar: kontrol kustom dengan nilai awal
yang diusulkan, atau kontrol yang menghilangkan currentValue, dapat membuka editor kontrol.
Kontrol manual Sesuaikan, ciutkan, dan Option-klik hanya tersedia di Codex atau di localhost. Pada situs yang dihosting di ChatGPT, kontrol kustom terdaftar muncul secara otomatis tanpa tombol Sesuaikan atau ciutkan. Mode lanjutan yang diminta halaman tetap berfungsi di sana.
Tambahkan kontrol kustom
Gunakan registerControls() untuk mengaitkan kontrol anotasi dengan satu atau beberapa elemen
DOM. Kontrol dapat mempratinjau properti aplikasi, seperti token jarak,
atau mengumpulkan pilihan untuk disertakan dalam permintaan, seperti nada bahasa email.
Pratinjau token jarak bersama
Kedua kartu dalam contoh ini menggunakan properti CSS yang sama:
<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>Jalankan skrip ini setelah membuat pratinjau:
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);
}Beri anotasi pada salah satu kartu dan ubah Padding kartu (piksel) dari 16 menjadi 24. Pada
situs yang dihosting di ChatGPT, kontrol muncul secara otomatis; di Codex atau di
localhost, pilih Sesuaikan jika diperlukan. Kedua kartu diperbarui. Anotasi mencatat label, referensi,
serta nilai lama dan baru. Menghapus pratinjau memulihkan padding asli.
Panggil disposeAnnotationControls() saat menghapus komponen.
Kumpulkan pilihan tanpa pratinjau
Gunakan baris kontak dari contoh metadata untuk menawarkan nada bahasa email:
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",
},
],
});Kontrol ini tidak memerlukan penangan peristiwa karena tidak mempratinjau perubahan
halaman. Menghilangkan currentValue memberi tahu browser untuk menyertakan nada bahasa yang dipilih
meskipun pengguna mempertahankan opsi awal. Panggil registration?.dispose() saat
menghapus baris.
Untuk kontrol pilihan, callback pratinjau menerima option.value, seperti
"professional". Riwayat anotasi dan ChatGPT menerima
option.label yang terlihat, seperti "Professional", untuk opsi sebelumnya maupun opsi
yang dipilih. Gunakan label yang menjelaskan setiap pilihan; ID internal dalam value tidak
dikirim sebagai teks pilihan.
Konfigurasikan kontrol dan nilai awal
Gunakan controlsMode: "replace" untuk hanya menampilkan kontrol Anda bagi target
terdaftar, atau "extend" untuk menampilkannya bersama kontrol bawaan. Nilai opsional
controlsHeading memberi nama panel. Browser memangkas spasi di tepinya dan menerima satu hingga 80
karakter. Tanpa judul, panel menampilkan tag HTML elemen. Judul
tidak disertakan dalam konteks yang dikirim ke ChatGPT.
Satu pendaftaran mendukung hingga 12 kontrol. Masing-masing memerlukan type,
label yang terlihat, dan pengenal callback:
| Jenis | Nilai | Kolom tambahan |
|---|---|---|
color |
Warna heksadesimal, seperti "#2563eb" |
Tidak ada |
range |
Angka | min, max, dan step |
select |
String | options, array objek { label, value } |
toggle |
boolean | Tidak ada |
callback adalah pengenal string, bukan fungsi JavaScript. Pastikan nilainya unik
dalam pendaftaran, dimulai dengan huruf ASCII, dan menggunakan huruf ASCII,
angka, garis bawah, atau tanda hubung. Nilai opsional reference mengidentifikasi properti
yang diubah dan menyertai label serta nilai dalam anotasi.
Tetapkan currentValue ke nilai normal properti yang valid, termasuk pengeditan
yang belum disimpan. Jangan gunakan status pratinjau atau masukan yang belum selesai sebagai nilai dasar. Browser
menggunakannya untuk perubahan sebelum dan sesudah serta pengaturan ulang. Saat status aplikasi berubah, gunakan
registration.update({ controls }) untuk memperbarui nilai currentValue
kontrol. Pembaruan memengaruhi anotasi berikutnya; anotasi yang sudah ada mempertahankan
nilai yang telah direkam.
Tetapkan defaultValue untuk nilai awal yang disarankan; nilai ini lebih diutamakan daripada
currentValue untuk status awal
kontrol. Jika keduanya dihilangkan, kontrol dimulai dengan putih untuk warna, nilai minimum
untuk rentang, opsi pertama untuk pilihan, atau false untuk sakelar.
Pisahkan nilai yang diusulkan dari draf biasa. Jika pembaruan melempar kesalahan atau
permintaan gagal atau mengembalikan accepted: false, buang usulan yang dicoba dan
pulihkan kontrol serta status usulan sebelumnya. Pertahankan usulan saat permintaan
diterima: browser mungkin memasukkannya ke antrean sebelum merekam kontrol.
Validasi kontrol dan pendaftaran
Browser memvalidasi pendaftaran dan pembaruan berdasarkan batasan berikut:
| Kolom atau sumber daya | Batasan |
|---|---|
| Kontrol | Hingga 12 per pendaftaran, dengan pengenal callback yang unik. Gunakan hanya kolom yang ditentukan untuk jenis kontrol tersebut. |
| Label dan judul | Label kontrol, label opsi, dan controlsHeading tidak boleh kosong setelah spasi di tepinya dipangkas dan panjangnya paling banyak 80 unit kode UTF-16. Teks kontrol tidak boleh berisi karakter kontrol atau karakter pengubah arah. |
| Pengenal | callback panjangnya paling banyak 80 unit kode UTF-16 setelah spasi di tepinya dipangkas, menggunakan format yang dijelaskan di atas. reference panjangnya 1–80 karakter dan mengizinkan huruf ASCII, angka, serta _ . / : @ $ # -, tanpa spasi. |
| Opsi pilihan | 1–12 opsi dengan string value yang berbeda dan panjangnya paling banyak 512 unit kode UTF-16. currentValue dan defaultValue yang diberikan harus cocok dengan nilai salah satu opsi. |
| Nilai rentang | min, max, currentValue, dan defaultValue harus berupa angka berhingga antara −10.000 dan 10.000. Memerlukan min < max, step dari 0,001 hingga 10.000 yang tidak lebih besar dari max - min, serta nilai awal dalam rentang. Nilai awal tidak harus selaras dengan step. |
| Warna dan sakelar | Warna harus menggunakan 3, 4, 6, atau 8 digit heksadesimal setelah #. Nilai sakelar harus berupa true atau false. |
| Target | 1–128 entri target saat mendaftar, semuanya merupakan elemen dalam dokumen saat ini. Pembaruan dapat meneruskan targets: [] untuk melepaskan kaitan. Setiap dokumen mendukung hingga 64 pendaftaran kontrol dan 1.024 kaitan pendaftaran ke target. |
| Kontrol yang diserialisasi | Payload JSON yang berisi controls, controlsHeading, dan controlsMode harus muat dalam 16.384 unit kode UTF-16. Gunakan data yang dapat dikodekan sebagai JSON, tanpa fungsi, simbol, atau bilangan bulat besar. |
registerControls() dan registration.update() dapat melempar kesalahan secara sinkron untuk
definisi atau target yang tidak valid, atau batas yang terlampaui. Pembaruan setelah dispose()
juga melempar kesalahan. Pembaruan yang ditolak mempertahankan pendaftaran sebelumnya, termasuk
kontrol dan targetnya. Tangani kegagalan di lokasi pemanggilan, pastikan pengeditan biasa
tetap dapat digunakan, dan gunakan kembali atau hapus pendaftaran yang dimiliki agar tetap berada dalam
batasan.
Tangani pratinjau dan pengaturan ulang
Peristiwa oaiannotationcontrolchange merambat ke atas dari elemen yang dipilih.
detail miliknya berisi callback, value, dan action:
| Tindakan | Terapkan nilai yang diberikan untuk |
|---|---|
preview |
Menampilkan perubahan yang diminta. |
preview-original |
Menampilkan sementara status asli untuk perbandingan. |
reset |
Memulihkan status asli saat pratinjau dihapus. |
Terapkan nilai yang diberikan untuk setiap tindakan, seperti pada contoh jarak. Buat penangan yang dapat dibalik dan aman untuk dipanggil berulang kali. Peristiwa pratinjau tidak meminta perubahan permanen; simpan melalui alur penyimpanan normal aplikasi Anda. Pisahkan draf biasa, masukan yang belum selesai, dan pratinjau agar pengeditan yang tidak terkait tidak menimpa pratinjau. Saat seseorang mengedit pengaturan yang sama, ganti pratinjaunya dan perbarui nilai dasar untuk anotasi berikutnya.
Simpan handle pendaftaran untuk pembaruan dan pembersihan. update() menerima
kombinasi apa pun dari targets, controls, controlsHeading, dan controlsMode.
Kolom yang dihilangkan mempertahankan nilai sebelumnya. Misalnya, gunakan
registration.update({ targets: newElement }) saat mengganti elemen DOM suatu
komponen. Kumpulan target merekam elemen yang sudah ada dan tidak melacak kecocokan
selektor di masa mendatang.
Teruskan targets: [] untuk melepaskan kaitan pendaftaran atau controls: [] untuk mengosongkan
kontrolnya. Panggil dispose() dan hapus pendengar peristiwa saat menghapus
integrasi. Pembersihan juga harus memulihkan perenderan normal: dispose() hanya
menghapus pendaftaran kontrol dan tidak membatalkan perubahan pratinjau Anda. Contoh jarak
menghapus penimpaan inline-nya untuk memulihkan nilai CSS asli. Kedua metode
kembali secara sinkron tanpa nilai. Pembaruan memengaruhi
pilihan berikutnya; anotasi tersimpan mempertahankan kontrol dan target yang telah direkam.
Peristiwa kontrol menargetkan elemen yang direkam oleh anotasi. Memperbarui target pendaftaran tidak mengalihkan anotasi yang sudah ada. Peristiwa pengaturan ulang masih dapat dipicu pada elemen yang telah dihapus, sehingga pendengar pada induknya yang lama tidak akan menerimanya.
Buat objek kanvas dapat dipilih
Permukaan anotasi memungkinkan aplikasi Anda mengidentifikasi objek individual yang digambar
di dalam kanvas. Daftarkan elemen host dengan registerSurface() dan sediakan
fungsi hitTest yang mengembalikan objek dengan id yang stabil, atau null untuk
ruang kosong.
Host harus berupa elemen HTML yang terhubung di luar shadow DOM dalam dokumen tingkat teratas yang aman. Pendaftaran permukaan tidak didukung di dalam iframe.
Contoh ini menggambar batang pendapatan dan membuatnya dapat dipilih. Letakkan skrip setelah kanvas:
<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"
);
},
});Mengarahkan penunjuk ke batang dalam mode Anotasi akan menyorotnya. Memilihnya akan membuka anotasi dengan nama objek, metadata, dan tangkapan layar pilihan.
Pastikan ID tetap stabil dalam suatu permukaan. Nilai opsional name terlihat oleh pengguna;
role memberikan deskripsi semantik singkat. Nilai opsional rect menggunakan piksel CSS
relatif terhadap area halaman yang terlihat, sesuai dengan clientX dan clientY. Konversikan dari koordinat adegan,
termasuk skala, pergeseran, dan zoom.
hitTest dapat mengembalikan promise dan menerima AbortSignal sebagai signal untuk
membatalkan pekerjaan yang telah digantikan. Browser memberikan waktu 250 milidetik sebelum
kembali menggunakan pemilihan DOM. Kesalahan dan hasil yang tidak valid juga memicu mekanisme pengganti ini. Kembalikan
null secara eksplisit untuk uji deteksi objek yang berhasil tanpa menemukan objek.
Pekerjaan yang dibatalkan tetap harus menyelesaikan atau menolak promise-nya. Browser hanya mengizinkan
satu callback hitTest yang sedang berjalan dan mempertahankan slot tersebut hingga promise
selesai, bahkan setelah pembatalan atau waktu habis. Jika worker menangani pemilihan,
selesaikan promise yang menunggu saat pekerjaannya dibatalkan; membuang respons worker
yang dibatalkan dapat memblokir pemilihan kanvas berikutnya.
Gunakan callback opsional renderSelection untuk umpan balik khusus aplikasi.
Hapus umpan balik ketika kedua ID bernilai null. Panggil surface?.invalidate() setelah
memindahkan objek atau mengubah zoom, dan surface?.dispose() saat menghapus
integrasi. Keduanya kembali secara sinkron tanpa nilai.
Tambahkan kontrol ke objek kanvas
Daftarkan kontrol kustom pada elemen DOM permukaan. Objek yang digambar, yang disebut objek virtual dalam API, menggunakan kontrol kustom Anda; kontrol CSS dan teks bawaan tidak berlaku untuk objek tersebut.
Untuk beberapa objek, gunakan renderSelection untuk memperbarui kontrol host saat
selectedId berubah, sebelum browser merekam anotasi. Peristiwa
kontrol menyertakan detail.virtualTarget: { surfaceId, targetId }. Arahkan setiap peristiwa
menggunakan identitas yang telah direkam tersebut beserta callback miliknya, bukan pilihan
saat ini. targetId cocok dengan id dari hitTest; surfaceId mengidentifikasi
pendaftaran permukaan browser. Peristiwa DOM biasa tidak menyertakan virtualTarget.
Peristiwa pratinjau, perbandingan, dan pengaturan ulang mempertahankan identitas objek asli setelah objek lain dipilih. Membuka kembali anotasi tersimpan tidak menjalankan ulang uji deteksi objek untuk mengganti objek atau metadata yang telah direkam.
Kendalikan mode Anotasi
Gunakan toggle() untuk meminta perubahan mode, isActive() untuk membaca status yang dikonfirmasi,
dan peristiwa oaiannotationmodechange pada dokumen agar UI Anda tetap sinkron.
Deteksi ketersediaan kedua metode untuk browser lama atau yang tidak didukung. Tambahkan tombol ini,
lalu jalankan skrip setelah tombol tersedia:
<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() membalik mode. Teruskan true untuk memastikannya aktif atau false untuk memastikannya
nonaktif. Permintaan berulang dengan boolean yang sama bersifat idempoten. Meneruskan
true mempertahankan editor yang aktif atau aktivasi yang menunggu; false membatalkan
aktivasi yang menunggu dan menggunakan alur keluar normal.
Panggil toggle() dari interaksi pengguna yang sedang berlangsung. Hasil sinkron { accepted }
mengakui penerimaan permintaan, bukan mengonfirmasi perubahan mode. Pemeriksaan kelayakan
browser masih dapat mencegah perubahan tersebut. Baca status dari isActive()
dan peristiwa, seperti pada contoh.
isActive() tidak memerlukan gestur pengguna. Browser memperbaruinya sebelum mengirim
oaiannotationmodechange, yang event.detail.active miliknya berupa boolean. Browser
tidak mengirim peristiwa awal atau duplikat untuk operasi paksa yang tidak mengubah apa pun, jadi inisialisasi UI Anda
dari getter. Jika akses dicabut saat aktif, peristiwa terakhir melaporkan
active: false dan getter yang dipertahankan mengembalikan false.
Mode Anotasi menangkap klik halaman. Tahan Spasi untuk menggunakan kontrol halaman,
termasuk tombol keluar Anda, atau keluar melalui UI anotasi browser.
Menahan Spasi saja membuat isActive() tetap bernilai true. API tidak mendukung
oai-annotation-ignore atau atribut lain yang memungkinkan klik diteruskan.
Keluar akan menutup editor dan mempertahankan anotasi tersimpan tanpa mengirimkannya
atau memindahkannya ke kolom penulisan pesan. Panggil cleanupAnnotationButton() saat
menghapus komponen. Referensi API yang telah direkam memungkinkan pembersihan menghapus
pendengar meskipun namespace telah hilang.
Uji integrasi Anda
Buka situs web Anda di browser bawaan aplikasi desktop dan uji fitur yang Anda tambahkan:
- Masuk ke mode Anotasi dan pilih objek serta teks. Periksa apakah sorotan, nama, rentang, dan metadata sesuai dengan target yang dimaksud.
- Buka anotasi dari tombol situs Anda. Periksa elemen atau rentang teks yang dipilih, komentar awal, dan mode editor.
- Ubah kontrol kustom, bandingkan dengan aslinya, dan hapus pratinjau. Periksa apakah aplikasi Anda memulihkan status asli.
- Untuk konten kanvas, uji ruang kosong, perubahan ukuran, dan perubahan adegan. Beralihlah antarobjek dan pastikan peristiwa kontrol tetap memperbarui target yang telah direkam. Batalkan uji deteksi objek asinkron dan pastikan pemilihan berikutnya tetap berfungsi.
- Simpan, buka kembali, dan edit anotasi dari pratinjau lampiran kolom penulisan pesan. Periksa apakah anotasi mempertahankan kontrol dan targetnya, serta apakah menghapusnya akan menghapus semua pratinjau.
- Kirim anotasi bersama pesan. Pastikan ChatGPT menerima
konten yang dipilih, metadata, dan nilai yang diminta, termasuk label pilihan
yang terlihat serta pilihan yang tidak berubah pada kontrol yang menghilangkan
currentValue. - Buka situs di browser tanpa API dan verifikasi bahwa interaksi normal tetap berfungsi.
Untuk menyediakan tindakan yang dapat dilakukan ChatGPT di situs web Anda, tambahkan Alat situs (WebMCP). Anotasi membawa pilihan pengguna dan masukan mereka ke dalam percakapan; alat situs memungkinkan agen bertindak berdasarkan konteks tersebut melalui kemampuan aplikasi Anda yang sudah ada.