Khả năng mở rộng chú thích

Browser Annotation API cho phép trang web của bạn tùy chỉnh những gì người dùng chọn, ngữ cảnh đi kèm phản hồi của họ và các thành phần điều khiển họ dùng để xem trước thay đổi trước khi gửi chú thích đến ChatGPT.

Chú thích trong trình duyệt hoạt động trên trang web của bạn mà không cần thay đổi mã. Người dùng có thể chọn một phần của trang, thêm bình luận và gửi phần đó trong Ngữ cảnh đến Codex hoặc ChatGPT Work.

Là nhà phát triển, bạn có thể sử dụng Browser Annotation API để cung cấp ngữ cảnh hoặc thành phần điều khiển dành riêng cho ứng dụng của mình. Ví dụ, bạn có thể đính kèm bản xem trước các biến thể thành phần trong bản xem trước hệ thống thiết kế để nhà phát triển biết cách cập nhật các thành phần trên trang web.

Để được hỗ trợ tìm hiểu Browser Annotation API hoặc thêm tính năng chú thích vào trang web, hãy cài đặt plugin Annotations Extensibility.

Cài đặt plugin Annotations Extensibility

Dùng thử

Xem Browser Annotation API hoạt động ngay trong hướng dẫn này.

  1. Mở trang này trong trình duyệt tích hợp của ChatGPT.
  2. Thử mở câu lệnh gợi ý sẽ xuất hiện trong thẻ này.
  3. Vào Chế độ chú thích, rồi chọn bảng bên dưới hoặc một mẫu mã để chuyển đổi giữa các bố cục và giao diện định sẵn.
  4. Bạn vẫn có thể chú thích bất kỳ mục nào trên trang và xem hành vi chú thích mặc định.

Mở trang này trong trình duyệt tích hợp của ChatGPT để thử chú thích.

Mở trong trình duyệt của ChatGPT

Chọn nội dung cần tùy chỉnh

Bắt đầu với cách tích hợp phù hợp với trang web của bạn:

Mục tiêu Cách tích hợp
Cho phép chọn một thẻ hoặc nhóm phần tử khác như một đối tượng duy nhất Đối tượng chọn
Cho phép người dùng chọn một cụm từ hoặc câu Vùng chứa chọn văn bản
Đính kèm ngữ cảnh bổ sung với vùng chọn Siêu dữ liệu vùng chọn
Mở chú thích từ nút của bạn với bình luận gợi ý Yêu cầu chú thích
Yêu cầu phản hồi về một đoạn văn bản cụ thể từ giao diện của bạn Yêu cầu cho phạm vi văn bản
Hiển thị các thành phần điều khiển nâng cao khi chú thích mở Chế độ mặc định của trình chỉnh sửa
Xem trước thuộc tính ứng dụng hoặc thu thập lựa chọn Thành phần điều khiển tùy chỉnh
Chọn từng đối tượng được vẽ trong canvas Bề mặt chú thích
Bật hoặc tắt Chế độ chú thích từ trang web của bạn Điều khiển Chế độ chú thích

Hướng dẫn này áp dụng cho bản phát hành DevDay 2026 của ứng dụng ChatGPT trên máy tính và các bản mới hơn. JavaScript API có sẵn thông qua document.oai.annotation trong trình duyệt tích hợp của ứng dụng, trên các trang cấp cao nhất an toàn như HTTPS hoặc localhost. Khi được bật, trình duyệt cài đặt API trước khi các tập lệnh của trang chạy. Kiểm tra sự hiện diện của từng phương thức để hỗ trợ các trình duyệt cũ hoặc không được hỗ trợ, rồi khởi tạo phần tích hợp khi các phần tử DOM của nó đã tồn tại. Không cần sự kiện báo sẵn sàng hoặc thăm dò định kỳ.

Các phương thức API trả về đồng bộ và các đối tượng quản lý đăng ký sẵn sàng để sử dụng ngay lập tức. Trình duyệt có thể hoàn tất việc tải trình chỉnh sửa chú thích sau đó. Hàm gọi lại hitTest của một bề mặt có thể trả về promise.

Tùy chỉnh đối tượng chọn

Theo mặc định, Chế độ chú thích chọn các phần tử từ DOM của trang, ưu tiên các đối tượng như văn bản, hình ảnh và thành phần điều khiển. Để cho phép chọn một đối tượng lớn hơn, đánh dấu vùng chứa đối tượng đó bằng oai-annotation-container và các phần tử con có thể chọn bằng oai-annotatable.

Ví dụ này cho phép chọn một thẻ biểu đồ như một đối tượng duy nhất:

<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>

Trỏ vào bất kỳ đâu trong thẻ sẽ làm nổi bật toàn bộ thẻ. Giá trị tùy chọn oai-annotatable đặt tên cho đối tượng để hiển thị cho người dùng và mô hình. Chọn tên giúp phân biệt các đối tượng gần nhau hoặc bỏ qua giá trị này.

Vùng chứa xác định nơi áp dụng các quy tắc chọn này. Một thuộc tính oai-annotatable đứng riêng không làm thay đổi hành vi chọn. Trong một vùng chứa, trình duyệt chọn đối tượng được đánh dấu gần nhất có chứa phần tử bên dưới con trỏ. Với các vùng chứa lồng nhau, vùng chứa gần nhất được sử dụng. Các khu vực không được đánh dấu bên trong một vùng chứa sẽ tạo chú thích trang web; các khu vực nằm ngoài tất cả vùng chứa vẫn giữ hành vi mặc định.

Bật tính năng chọn văn bản

Thêm oai-annotation-container-text vào một vùng để cho phép người dùng kéo để chọn văn bản trong Chế độ chú thích. Thuộc tính này không cần giá trị:

<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>

Thả chuột sau khi chọn một vùng không trống sẽ mở trình chỉnh sửa chú thích văn bản với phạm vi đã chọn và ngữ cảnh của nó. Nhấp mà không chọn văn bản sẽ không tạo chú thích. Escape hoặc thao tác bị hủy sẽ hủy vùng chọn.

Vùng chứa chọn văn bản hoặc DOM gần nhất quyết định cách thao tác bắt đầu. Vùng chứa văn bản không sử dụng dấu đánh dấu oai-annotatable để chọn phần tử. Lồng một oai-annotation-container để khôi phục khả năng chọn phần tử, hoặc một vùng chứa văn bản để khôi phục khả năng chọn văn bản. Nếu cả hai thuộc tính cùng nằm trên một phần tử, tính năng chọn văn bản được ưu tiên.

Việc chọn văn bản tuân theo các quy tắc chọn thông thường của trang và có thể mở rộng ra ngoài vùng chứa ban đầu. Vùng chứa không giới hạn phạm vi hoặc bật tính năng chọn bên trong iframe. Các trường văn bản vẫn có thể được chọn, nhưng các lần nhấp thông thường trên trang và thao tác gốc trên thành phần điều khiển vẫn bị chặn trong Chế độ chú thích. Các lệnh gọi request() tường minh vẫn giữ nguyên hành vi hiện có.

Thêm ngữ cảnh vào vùng chọn

Thêm oai-annotation-metadata vào một đối tượng đã đánh dấu để đính kèm ngữ cảnh có thể không hiển thị trên trang. Ví dụ, một hàng liên hệ giả định có thể bao gồm địa chỉ email cho yêu cầu soạn 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>

Chọn một trong hai dòng sẽ chọn toàn bộ hàng. Siêu dữ liệu xuất hiện cùng chú thích và đi kèm chú thích trong cuộc trò chuyện. Chỉ đưa vào ngữ cảnh mà bạn muốn chia sẻ với cả người dùng và mô hình. Việc gửi email vẫn cần một công cụ email đã kết nối.

Sử dụng một đối tượng JSON nhỏ, phẳng với các giới hạn sau:

  • Tối đa sáu thuộc tính, với giá trị là chuỗi, số hữu hạn, boolean hoặc null.
  • Khóa dài tối đa 64 ký tự và giá trị chuỗi dài tối đa 256 ký tự.
  • Tối đa 2.048 byte cho đối tượng sau khi tuần tự hóa.

Khóa phải bắt đầu bằng một chữ cái ASCII và chỉ chứa chữ cái ASCII, chữ số, dấu cách, dấu gạch dưới hoặc dấu gạch nối. Dùng một dấu cách giữa các từ. Các đối tượng lồng nhau và mảng không được hỗ trợ. Trình duyệt bỏ qua siêu dữ liệu không hợp lệ.

Bạn cũng có thể cung cấp siêu dữ liệu thông qua request() hoặc kết quả hitTest của một bề mặt.

Mở chú thích từ trang web của bạn

Gọi document.oai.annotation.request(target, options) trực tiếp từ một thao tác của người dùng, chẳng hạn như nhấn nút. Đối tượng đích có thể là một phần tử HTML được gắn vào DOM nằm trong vùng hiển thị của tài liệu hiện tại hoặc một DOM Range. Đối tượng không cần các thuộc tính chú thích. ID đối tượng ảo không được hỗ trợ.

Các yêu cầu do trang web khởi tạo có thể cần sự cho phép của người dùng; người dùng có thể bật lại các tính năng chú thích bị chặn trong Công cụ trang web > Tính năng chú thích.

Thêm nút này bên cạnh thẻ biểu đồ trong ví dụ về lựa chọn, rồi chạy tập lệnh sau khi cả hai phần tử đã tồn tại:

<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.",
  });
});

Chọn Giải thích thêm sẽ yêu cầu trình duyệt mở chú thích với thẻ được chọn và một bình luận có thể chỉnh sửa. Người dùng có thể chỉnh sửa, lưu và gửi chú thích cùng tin nhắn của mình. Việc mở chú thích không gửi tin nhắn đến ChatGPT; chỉ người dùng mới có thể gửi.

Giữ lệnh gọi trong phạm vi tương tác đang diễn ra của người dùng. Nếu chờ một yêu cầu mạng hoàn tất trước, bạn có thể mất ngữ cảnh tương tác đó.

Đối số thứ hai là tùy chọn và hỗ trợ các trường sau:

Tùy chọn Hành vi
mode Sử dụng "advanced" để mở các thành phần điều khiển nâng cao cho một phần tử, hoặc "default" để sử dụng hành vi mặc định của trình chỉnh sửa. Mặc định là "default". Các thành phần điều khiển tùy chỉnh vẫn có thể mở với "default"; xem Chế độ mặc định của trình chỉnh sửa. Phạm vi văn bản chỉ hỗ trợ "default".
enterAnnotationMode Đặt thành true để vào Chế độ chú thích và tiếp tục ở chế độ đó sau khi hủy hoặc gửi chú thích.
metadata Siêu dữ liệu hợp lệ, không trống sẽ thay thế siêu dữ liệu HTML của đối tượng đích cho yêu cầu này. Tùy chọn này bị bỏ qua đối với phạm vi văn bản và khi phần tử đích nằm trong shadow DOM.
initialComment Cung cấp một bình luận có thể chỉnh sửa, dài tối đa 240 đơn vị mã UTF-16.

request() trả về một đối tượng có giá trị boolean accepted. Kiểm tra result.accepted, thay vì đối tượng kết quả, để biết trình duyệt đã nhận và xác thực yêu cầu hay chưa. Giá trị này không xác nhận rằng trình chỉnh sửa đã mở hoặc người dùng đã lưu hay gửi chú thích.

Trong khi trình chỉnh sửa tải, trình duyệt có thể giữ một yêu cầu đã được chấp nhận. Trình duyệt có thể từ chối yêu cầu khác khi đang có yêu cầu chờ xử lý, trình chỉnh sửa đang mở hoặc ChatGPT đang điều khiển trình duyệt.

Yêu cầu chú thích cho một phạm vi văn bản

Truyền một DOM Range để yêu cầu phản hồi về một đoạn văn bản mà không thay đổi vùng chọn văn bản của trình duyệt. Ví dụ này chọn nội dung của đoạn văn; không cần thuộc tính 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>

Chạy tập lệnh này sau khi cả hai phần tử đã tồn tại:

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.",
  });
});

Phạm vi phải chứa văn bản hiển thị không trống trong tài liệu hiện tại, với ít nhất một phần vùng chọn nằm trong vùng hiển thị của trang. Phạm vi có thể chứa tối đa 20.000 đơn vị mã UTF-16. Phạm vi thu gọn, văn bản chỉ có khoảng trắng, văn bản được chọn nhưng bị ẩn và phạm vi trong shadow root đóng không được hỗ trợ.

Yêu cầu cho phạm vi văn bản chỉ sử dụng trình chỉnh sửa văn bản mặc định. Yêu cầu có mode: "advanced" bị từ chối và siêu dữ liệu của yêu cầu bị bỏ qua. Giữ văn bản đích sẵn có trong khi yêu cầu đã được chấp nhận chờ trình chỉnh sửa: trình duyệt kiểm tra lại phạm vi trước khi mở. Vùng chứa chọn văn bản là cơ chế riêng cho phép người dùng kéo để chọn văn bản trong Chế độ chú thích.

Chọn chế độ mặc định của trình chỉnh sửa

Để hiển thị ngay các thành phần điều khiển nâng cao cho chú thích được mở qua giao diện chọn của trình duyệt, thêm thẻ này vào <head> của trang:

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

Với chú thích được mở từ giao diện của bạn, truyền { mode: "advanced" } cho request(). Dùng mode: "default" hoặc bỏ qua để sử dụng hành vi mặc định của trình chỉnh sửa. Cài đặt meta của trang không ghi đè tùy chọn yêu cầu này. Chế độ mặc định không đảm bảo trình chỉnh sửa chỉ có phần bình luận: các thành phần điều khiển tùy chỉnh có giá trị khởi đầu được đề xuất, hoặc các thành phần điều khiển không có currentValue, có thể mở trình chỉnh sửa thành phần điều khiển.

Các thao tác thủ công Điều chỉnh, thu gọn và Option-click chỉ có trong Codex hoặc trên localhost. Trên các trang web được lưu trữ trực tuyến trong ChatGPT, các thành phần điều khiển tùy chỉnh đã đăng ký tự động xuất hiện mà không có nút Điều chỉnh hoặc thu gọn. Chế độ nâng cao do trang yêu cầu vẫn hoạt động ở đó.

Thêm thành phần điều khiển tùy chỉnh

Sử dụng registerControls() để liên kết các thành phần điều khiển chú thích với một hoặc nhiều phần tử DOM. Các thành phần điều khiển có thể xem trước thuộc tính ứng dụng, chẳng hạn như một token khoảng cách, hoặc thu thập lựa chọn để đưa vào yêu cầu, chẳng hạn như giọng văn của email.

Xem trước token khoảng cách dùng chung

Cả hai thẻ trong ví dụ này sử dụng cùng một thuộc tính 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>

Chạy tập lệnh này sau khi tạo bản xem trước:

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);
}

Chú thích một trong hai thẻ và đổi Khoảng đệm thẻ (pixel) từ 16 thành 24. Trên các trang web được lưu trữ trực tuyến trong ChatGPT, các thành phần điều khiển tự động xuất hiện; trong Codex hoặc trên localhost, chọn Điều chỉnh nếu cần. Cả hai thẻ đều cập nhật. Chú thích ghi lại nhãn, tham chiếu, và các giá trị cũ, mới. Xóa bản xem trước sẽ khôi phục khoảng đệm ban đầu. Gọi disposeAnnotationControls() khi gỡ bỏ thành phần.

Thu thập lựa chọn mà không có bản xem trước

Sử dụng hàng liên hệ trong ví dụ về siêu dữ liệu để cung cấp lựa chọn giọng văn 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",
    },
  ],
});

Thành phần điều khiển này không cần trình xử lý sự kiện vì nó không xem trước thay đổi trên trang. Bỏ qua currentValue sẽ yêu cầu trình duyệt đưa giọng văn đã chọn vào chú thích ngay cả khi người dùng giữ nguyên lựa chọn ban đầu. Gọi registration?.dispose() khi gỡ bỏ hàng.

Với thành phần điều khiển dạng danh sách chọn, các hàm gọi lại xem trước nhận option.value, chẳng hạn như "professional". Lịch sử chú thích và ChatGPT nhận option.label hiển thị, chẳng hạn như "Professional", cho cả lựa chọn trước đó và lựa chọn đã chọn. Sử dụng nhãn giải thích từng lựa chọn; ID nội bộ trong value không được gửi dưới dạng văn bản của lựa chọn.

Cấu hình thành phần điều khiển và giá trị khởi đầu

Sử dụng controlsMode: "replace" để chỉ hiển thị các thành phần điều khiển của bạn cho các đối tượng đích đã đăng ký, hoặc "extend" để hiển thị chúng cùng các thành phần điều khiển tích hợp. Giá trị tùy chọn controlsHeading đặt tên cho bảng điều khiển. Trình duyệt cắt khoảng trắng ở hai đầu và chấp nhận từ một đến 80 ký tự. Nếu không có tiêu đề, bảng điều khiển hiển thị thẻ HTML của phần tử. Tiêu đề không được đưa vào ngữ cảnh gửi đến ChatGPT.

Một đăng ký hỗ trợ tối đa 12 thành phần điều khiển. Mỗi thành phần yêu cầu một type, label hiển thị và mã định danh callback:

Loại Giá trị Các trường bổ sung
color Màu thập lục phân, chẳng hạn như "#2563eb" Không có
range Số min, max và step
select Chuỗi options, một mảng các đối tượng { label, value }
toggle boolean Không có

callback là mã định danh dạng chuỗi, không phải hàm JavaScript. Đảm bảo mã này là duy nhất trong đăng ký, bắt đầu bằng một chữ cái ASCII và sử dụng chữ cái ASCII, chữ số, dấu gạch dưới hoặc dấu gạch nối. Giá trị tùy chọn reference xác định thuộc tính đang được thay đổi và đi kèm nhãn cùng giá trị trong chú thích.

Đặt currentValue thành giá trị thông thường hợp lệ của thuộc tính, bao gồm cả các chỉnh sửa chưa lưu. Không dùng trạng thái xem trước hoặc dữ liệu nhập chưa hoàn tất làm giá trị cơ sở. Trình duyệt sử dụng giá trị này để xác định thay đổi trước và sau cũng như đặt lại. Khi trạng thái ứng dụng thay đổi, sử dụng registration.update({ controls }) để làm mới các giá trị currentValue của thành phần điều khiển. Cập nhật ảnh hưởng đến các chú thích trong tương lai; chú thích hiện có giữ nguyên các giá trị đã ghi lại.

Đặt defaultValue để đề xuất giá trị khởi đầu; giá trị này được ưu tiên hơn currentValue khi xác định trạng thái ban đầu của thành phần điều khiển. Nếu bỏ qua cả hai, thành phần điều khiển bắt đầu bằng màu trắng đối với màu sắc, giá trị tối thiểu đối với khoảng giá trị, lựa chọn đầu tiên đối với danh sách chọn hoặc false đối với công tắc.

Giữ các giá trị đề xuất tách biệt với bản nháp thông thường. Nếu một lần cập nhật phát sinh ngoại lệ hoặc một yêu cầu thất bại hay trả về accepted: false, hãy loại bỏ đề xuất vừa thử và khôi phục các thành phần điều khiển cùng trạng thái đề xuất trước đó. Giữ lại đề xuất khi yêu cầu được chấp nhận: trình duyệt có thể đưa yêu cầu vào hàng đợi trước khi ghi lại các thành phần điều khiển.

Xác thực thành phần điều khiển và đăng ký

Trình duyệt xác thực các đăng ký và cập nhật theo những giới hạn sau:

Trường hoặc tài nguyên Ràng buộc
Thành phần điều khiển Tối đa 12 thành phần cho mỗi đăng ký, với mã định danh callback duy nhất. Chỉ sử dụng các trường được định nghĩa cho loại thành phần điều khiển đó.
Nhãn và tiêu đề Nhãn thành phần điều khiển, nhãn lựa chọn và controlsHeading phải không trống sau khi cắt khoảng trắng ở hai đầu và dài tối đa 80 đơn vị mã UTF-16. Văn bản của thành phần điều khiển không được chứa ký tự điều khiển hoặc ký tự đổi hướng văn bản.
Mã định danh callback dài tối đa 80 đơn vị mã UTF-16 sau khi cắt khoảng trắng ở hai đầu, sử dụng định dạng đã mô tả ở trên. reference dài 1–80 ký tự và cho phép chữ cái ASCII, chữ số và _ . / : @ $ # -, không có dấu cách.
Các lựa chọn trong danh sách chọn 1–12 lựa chọn với các chuỗi value khác nhau, dài tối đa 512 đơn vị mã UTF-16. Các giá trị currentValue và defaultValue được cung cấp phải khớp với giá trị của một lựa chọn.
Giá trị khoảng min, max, currentValue và defaultValue phải là số hữu hạn trong khoảng −10.000 đến 10.000. Yêu cầu min < max, một step từ 0,001 đến 10.000 không lớn hơn max - min và các giá trị khởi đầu nằm trong khoảng. Giá trị khởi đầu không cần khớp với bước step.
Màu sắc và công tắc Màu sắc phải sử dụng 3, 4, 6 hoặc 8 chữ số thập lục phân sau #. Giá trị công tắc phải là true hoặc false.
Đối tượng đích 1–128 mục đối tượng đích khi đăng ký, tất cả đều là phần tử trong tài liệu hiện tại. Khi cập nhật, có thể truyền targets: [] để hủy liên kết. Mỗi tài liệu hỗ trợ tối đa 64 đăng ký thành phần điều khiển và 1.024 liên kết giữa đăng ký và đối tượng đích.
Thành phần điều khiển đã tuần tự hóa Dữ liệu JSON chứa controls, controlsHeading và controlsMode phải nằm trong giới hạn 16.384 đơn vị mã UTF-16. Sử dụng dữ liệu có thể mã hóa thành JSON, không có hàm, symbol hoặc số nguyên lớn.

registerControls() và registration.update() có thể phát sinh ngoại lệ đồng bộ khi định nghĩa hoặc đối tượng đích không hợp lệ, hay vượt quá giới hạn. Cập nhật sau dispose() cũng phát sinh ngoại lệ. Một cập nhật bị từ chối sẽ giữ nguyên đăng ký trước đó, bao gồm các thành phần điều khiển và đối tượng đích. Xử lý lỗi tại nơi gọi, đảm bảo tính năng chỉnh sửa thông thường vẫn sử dụng được, đồng thời tái sử dụng hoặc giải phóng các đăng ký do bạn quản lý để không vượt quá giới hạn.

Xử lý xem trước và đặt lại

Sự kiện oaiannotationcontrolchange nổi bọt từ phần tử được chọn. Thuộc tính detail của sự kiện chứa callback, value và action:

Hành động Áp dụng giá trị được cung cấp để
preview Hiển thị thay đổi được yêu cầu.
preview-original Tạm thời hiển thị trạng thái ban đầu để so sánh.
reset Khôi phục trạng thái ban đầu khi bản xem trước được xóa.

Áp dụng giá trị được cung cấp cho mọi hành động, như trong ví dụ về khoảng cách. Đảm bảo trình xử lý có thể đảo ngược tác động và an toàn khi được gọi nhiều lần. Các sự kiện xem trước không yêu cầu thay đổi vĩnh viễn; hãy lưu qua quy trình lưu thông thường của ứng dụng. Giữ bản nháp thông thường, dữ liệu nhập chưa hoàn tất và bản xem trước tách biệt để các chỉnh sửa không liên quan không ghi đè bản xem trước. Khi người dùng chỉnh sửa cùng một cài đặt, thay thế bản xem trước của cài đặt đó và cập nhật giá trị cơ sở cho các chú thích trong tương lai.

Giữ lại đối tượng quản lý đăng ký để cập nhật và dọn dẹp. update() chấp nhận mọi tổ hợp của targets, controls, controlsHeading và controlsMode. Các trường bị bỏ qua giữ nguyên giá trị trước đó. Ví dụ, sử dụng registration.update({ targets: newElement }) khi thay thế phần tử DOM của một thành phần. Tập hợp đối tượng đích ghi nhận các phần tử hiện có và không theo dõi các phần tử khớp với bộ chọn trong tương lai.

Truyền targets: [] để hủy liên kết đăng ký hoặc controls: [] để xóa các thành phần điều khiển. Gọi dispose() và gỡ bỏ các bộ lắng nghe sự kiện khi gỡ bỏ phần tích hợp. Việc dọn dẹp cũng phải khôi phục cách hiển thị thông thường: dispose() chỉ gỡ bỏ đăng ký thành phần điều khiển và không hoàn tác các thay đổi xem trước của bạn. Ví dụ về khoảng cách gỡ bỏ phần ghi đè nội tuyến để khôi phục giá trị CSS ban đầu. Cả hai phương thức đều trả về đồng bộ mà không có giá trị. Các cập nhật ảnh hưởng đến những lần chọn sau này; chú thích đã lưu giữ nguyên các thành phần điều khiển và đối tượng đích đã ghi lại.

Sự kiện thành phần điều khiển nhắm đến phần tử được chú thích ghi lại. Việc cập nhật các đối tượng đích của đăng ký không chuyển hướng các chú thích hiện có. Sự kiện đặt lại vẫn có thể phát sinh trên một phần tử đã bị gỡ bỏ, nên bộ lắng nghe trên phần tử cha cũ sẽ không nhận được chúng.

Cho phép chọn các đối tượng canvas

Bề mặt chú thích cho phép ứng dụng của bạn xác định từng đối tượng được vẽ trong canvas. Đăng ký phần tử chủ bằng registerSurface() và cung cấp một hàm hitTest trả về đối tượng có id ổn định, hoặc null cho vùng trống.

Phần tử chủ phải là phần tử HTML được gắn vào DOM, nằm ngoài shadow DOM trong một tài liệu cấp cao nhất an toàn. Việc đăng ký bề mặt không được hỗ trợ bên trong iframe.

Ví dụ này vẽ một thanh doanh thu và cho phép chọn thanh đó. Đặt tập lệnh sau canvas:

<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"
    );
  },
});

Di chuột lên thanh trong Chế độ chú thích sẽ làm nổi bật thanh đó. Chọn thanh sẽ mở một chú thích với tên đối tượng, siêu dữ liệu và ảnh chụp màn hình của vùng chọn.

Giữ ID ổn định trong một bề mặt. Giá trị tùy chọn name hiển thị cho người dùng; role cung cấp mô tả ngữ nghĩa ngắn. Giá trị tùy chọn rect sử dụng pixel CSS tương đối với vùng hiển thị của trang, khớp với clientX và clientY. Chuyển đổi từ tọa độ cảnh, bao gồm tỷ lệ, dịch chuyển và thu phóng.

hitTest có thể trả về promise và nhận một AbortSignal dưới dạng signal để hủy công việc đã bị thay thế. Trình duyệt chờ tối đa 250 mili giây trước khi chuyển sang chọn DOM. Lỗi và kết quả không hợp lệ cũng dẫn đến việc chuyển sang cách chọn này. Trả về null một cách tường minh khi phép kiểm tra va chạm thành công nhưng không có đối tượng.

Công việc bị hủy vẫn phải hoàn tất promise bằng cách resolve hoặc reject. Trình duyệt chỉ cho phép một hàm gọi lại hitTest đang xử lý và giữ vị trí đó cho đến khi promise hoàn tất, ngay cả sau khi hủy hoặc hết thời gian chờ. Nếu một worker xử lý việc chọn, hãy hoàn tất promise đang chờ khi công việc của worker bị hủy; bỏ qua phản hồi của worker đã bị hủy có thể chặn các lần chọn canvas tiếp theo.

Sử dụng hàm gọi lại tùy chọn renderSelection để cung cấp phản hồi dành riêng cho ứng dụng. Xóa phản hồi khi cả hai ID đều là null. Gọi surface?.invalidate() sau khi di chuyển đối tượng hoặc thay đổi mức thu phóng, và surface?.dispose() khi gỡ bỏ phần tích hợp. Cả hai đều trả về đồng bộ mà không có giá trị.

Thêm thành phần điều khiển cho đối tượng canvas

Đăng ký các thành phần điều khiển tùy chỉnh trên phần tử DOM của bề mặt. Các đối tượng được vẽ, được gọi là đối tượng ảo trong API, sử dụng các thành phần điều khiển tùy chỉnh của bạn; các thành phần điều khiển CSS và văn bản tích hợp không áp dụng cho chúng.

Với nhiều đối tượng, sử dụng renderSelection để cập nhật các thành phần điều khiển của phần tử chủ khi selectedId thay đổi, trước khi trình duyệt ghi lại chú thích. Sự kiện thành phần điều khiển bao gồm detail.virtualTarget: { surfaceId, targetId }. Định tuyến từng sự kiện bằng danh tính đã ghi lại đó và callback của nó, thay vì vùng chọn hiện tại. targetId khớp với id từ hitTest; surfaceId xác định đăng ký bề mặt của trình duyệt. Các sự kiện DOM thông thường không có virtualTarget.

Các sự kiện xem trước, so sánh và đặt lại giữ nguyên danh tính của đối tượng ban đầu sau khi một đối tượng khác được chọn. Việc mở lại chú thích đã lưu không chạy lại phép kiểm tra va chạm để thay thế đối tượng hoặc siêu dữ liệu đã ghi lại.

Điều khiển Chế độ chú thích

Sử dụng toggle() để yêu cầu thay đổi chế độ, isActive() để đọc trạng thái đã xác nhận, và sự kiện oaiannotationmodechange của tài liệu để giữ giao diện đồng bộ. Kiểm tra sự hiện diện của cả hai phương thức để hỗ trợ các trình duyệt cũ hoặc không được hỗ trợ. Thêm nút này, rồi chạy tập lệnh sau khi nút đã tồn tại:

<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() đảo trạng thái chế độ. Truyền true để đảm bảo chế độ được bật hoặc false để đảm bảo chế độ được tắt. Các yêu cầu lặp lại với cùng một giá trị boolean có tính lũy đẳng. Truyền true sẽ giữ nguyên trình chỉnh sửa đang hoạt động hoặc yêu cầu kích hoạt đang chờ; false hủy yêu cầu kích hoạt đang chờ và sử dụng quy trình thoát thông thường.

Gọi toggle() từ một thao tác đang diễn ra của người dùng. Kết quả đồng bộ { accepted } của phương thức xác nhận đã tiếp nhận yêu cầu, không xác nhận chế độ đã thay đổi. Các bước kiểm tra điều kiện của trình duyệt vẫn có thể ngăn thay đổi. Đọc trạng thái từ isActive() và sự kiện, như trong ví dụ.

isActive() không cần thao tác của người dùng. Trình duyệt cập nhật giá trị này trước khi phát oaiannotationmodechange, có event.detail.active là giá trị boolean. Trình duyệt không gửi sự kiện ban đầu hoặc sự kiện trùng lặp khi yêu cầu cưỡng chế không làm thay đổi trạng thái, vì vậy hãy khởi tạo giao diện bằng hàm getter. Nếu quyền truy cập bị thu hồi khi chế độ đang hoạt động, sự kiện cuối cùng báo active: false và hàm getter được giữ lại trả về false.

Chế độ chú thích chặn các lần nhấp trên trang để xử lý. Giữ Space để sử dụng các thành phần điều khiển trên trang, bao gồm nút thoát của bạn, hoặc thoát qua giao diện chú thích của trình duyệt. Chỉ giữ Space vẫn để isActive() ở giá trị true. API không hỗ trợ oai-annotation-ignore hoặc thuộc tính khác cho phép các lần nhấp đi xuyên qua.

Thoát sẽ đóng trình chỉnh sửa và giữ nguyên các chú thích đã lưu mà không gửi chúng hoặc chuyển chúng vào trình soạn tin nhắn. Gọi cleanupAnnotationButton() khi gỡ bỏ thành phần. Tham chiếu API đã lưu cho phép quá trình dọn dẹp gỡ bỏ các bộ lắng nghe ngay cả khi không gian tên đã biến mất.

Kiểm thử phần tích hợp

Mở trang web của bạn trong trình duyệt tích hợp của ứng dụng trên máy tính và kiểm thử các tính năng bạn đã thêm:

  1. Vào Chế độ chú thích và chọn các đối tượng cùng văn bản. Kiểm tra xem phần làm nổi bật, tên, phạm vi và siêu dữ liệu có khớp với các đối tượng đích dự kiến hay không.
  2. Mở chú thích từ nút trên trang web của bạn. Kiểm tra phần tử hoặc phạm vi văn bản đã chọn, bình luận ban đầu và chế độ của trình chỉnh sửa.
  3. Thay đổi một thành phần điều khiển tùy chỉnh, so sánh với bản gốc và xóa bản xem trước. Kiểm tra xem ứng dụng có khôi phục trạng thái ban đầu hay không.
  4. Với nội dung canvas, kiểm thử vùng trống, việc đổi kích thước và thay đổi cảnh. Chuyển đổi đối tượng và xác nhận các sự kiện thành phần điều khiển vẫn cập nhật đúng đối tượng đích đã ghi lại. Hủy một phép kiểm tra va chạm bất đồng bộ và xác nhận các lần chọn sau vẫn hoạt động.
  5. Lưu, mở lại và chỉnh sửa chú thích từ bản xem trước tệp đính kèm của trình soạn tin nhắn. Kiểm tra xem chú thích có giữ nguyên các thành phần điều khiển và đối tượng đích hay không, và việc xóa chú thích có xóa mọi bản xem trước hay không.
  6. Gửi chú thích cùng một tin nhắn. Xác nhận ChatGPT nhận được nội dung đã chọn, siêu dữ liệu và các giá trị được yêu cầu, bao gồm nhãn hiển thị của các lựa chọn và các lựa chọn không thay đổi trên những thành phần điều khiển không có currentValue.
  7. Mở trang web trong một trình duyệt không có API và xác minh các tương tác thông thường vẫn hoạt động.

Để cung cấp các hành động mà ChatGPT có thể thực hiện trên trang web của bạn, hãy thêm Công cụ trang web (WebMCP). Chú thích đưa vùng chọn và phản hồi của người dùng vào cuộc trò chuyện; công cụ trang web cho phép tác nhân hành động dựa trên ngữ cảnh đó thông qua các khả năng hiện có của ứng dụng.