Extensibilidad de las anotaciones
La Browser Annotation API permite que tu sitio web personalice qué seleccionan las personas, qué contexto acompaña sus comentarios y qué controles usan para previsualizar los cambios antes de enviar una anotación a ChatGPT.
Las anotaciones del navegador funcionan en tu sitio sin cambios en el código. Las personas pueden seleccionar una parte de una página, añadir un comentario y enviarlo en Context a Codex o ChatGPT Work.
Como desarrollador, puedes usar la Browser Annotation API para proporcionar contexto o controles específicos de tu aplicación. Por ejemplo, puedes adjuntar vistas previas de variantes de componentes en una vista previa de un sistema de diseño para que los desarrolladores sepan cómo actualizar los componentes de un sitio web.
Para obtener ayuda para comprender la Browser Annotation API o añadir compatibilidad con las anotaciones a tu sitio web, instala el plugin Annotations Extensibility.
Instalar el plugin Annotations Extensibility
Pruébalo
Observa la Browser Annotation API en acción en esta guía.
- Abre esta página en el navegador integrado de ChatGPT.
- Prueba a abrir el prompt sugerido que aparecerá en esta tarjeta.
- Entra en el modo de anotación y selecciona la tabla siguiente o un ejemplo de código para alternar entre diseños y temas predefinidos.
- Puedes seguir anotando cualquier elemento de la página y observar el comportamiento predeterminado de las anotaciones.
Abre esta página en el navegador integrado de ChatGPT para probar las anotaciones.
Abrir en el navegador de ChatGPT
Elige qué personalizar
Empieza con la integración que se adapte a tu sitio web:
| Objetivo | Integración |
|---|---|
| Permitir seleccionar una tarjeta u otro grupo de elementos como un solo objeto | Objetivos de selección |
| Permitir que las personas seleccionen una frase u oración | Contenedores de selección de texto |
| Incluir contexto adicional con una selección | Metadatos de selección |
| Abrir una anotación desde tu propio botón con un comentario sugerido | Solicitudes de anotación |
| Solicitar comentarios sobre un pasaje exacto desde tu propia interfaz | Solicitudes de rangos de texto |
| Mostrar controles avanzados al abrir una anotación | Valores predeterminados del editor |
| Previsualizar propiedades de la aplicación o recopilar elecciones | Controles personalizados |
| Seleccionar objetos individuales dibujados dentro de un lienzo | Superficies de anotación |
| Activar o desactivar el modo de anotación desde tu sitio | Controles del modo de anotación |
Esta guía está dirigida a la versión de DevDay 2026 de la aplicación de escritorio de ChatGPT y a las posteriores.
La API de JavaScript está disponible a través de document.oai.annotation en el navegador
integrado de la aplicación, en páginas seguras de nivel superior, como HTTPS o localhost. Cuando
está habilitada, el navegador la instala antes de ejecutar los scripts de tu página. Detecta la disponibilidad de
cada método para admitir navegadores antiguos o no compatibles y, después, inicializa tu
integración una vez que existan sus elementos DOM. No se necesita ningún evento de disponibilidad ni sondeo.
Los métodos de la API devuelven resultados de forma síncrona y los identificadores de registro están listos para usarse
inmediatamente. El navegador puede terminar de cargar el editor de anotaciones después.
La función de devolución de llamada hitTest de una superficie puede devolver una promesa.
Personaliza los objetivos de selección
De forma predeterminada, el modo de anotación selecciona elementos del DOM de la página y da preferencia a
objetivos como texto, imágenes y controles. Para permitir la selección de un objeto más grande,
marca la región que lo contiene con oai-annotation-container y sus descendientes
seleccionables con oai-annotatable.
Este ejemplo permite seleccionar una tarjeta de gráfico como un solo objeto:
<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>Al apuntar a cualquier lugar de la tarjeta, se resalta toda la tarjeta. El valor opcional de
oai-annotatable da al objeto un nombre que se muestra al usuario y al modelo.
Elige nombres que distingan los objetos cercanos u omite el valor.
El contenedor define dónde se aplican estas reglas de selección. Un
atributo oai-annotatable por sí solo no cambia el comportamiento de selección.
Dentro de un contenedor, el navegador selecciona el objetivo marcado más cercano que contiene
el elemento bajo el puntero. Los contenedores anidados usan el contenedor más cercano.
Las áreas sin marcar dentro de un contenedor crean una anotación de página web; las áreas fuera de
todos los contenedores mantienen el comportamiento predeterminado.
Habilita la selección de texto
Añade oai-annotation-container-text a una región para permitir que las personas arrastren el puntero para seleccionar texto
durante el modo de anotación. El atributo no necesita un valor:
<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>Al soltar una selección no vacía, se abre el editor de anotaciones de texto con el rango seleccionado y su contexto. Hacer clic sin seleccionar texto no crea una anotación. Escape o un gesto cancelado cancelan la selección.
El contenedor de selección de texto o DOM más cercano determina cómo comienza un gesto.
Los contenedores de texto no usan marcadores oai-annotatable para seleccionar elementos. Anida
un oai-annotation-container para restablecer la selección de elementos o un contenedor de texto para
restablecer la selección de texto. Si ambos atributos están en un mismo elemento, la selección de texto
tiene prioridad.
La selección de texto sigue las reglas normales de selección de la página y puede extenderse más allá
del contenedor inicial. Un contenedor no recorta el rango ni habilita la selección
dentro de un iframe. Los campos de texto siguen siendo seleccionables, pero los clics habituales en la página y
las acciones nativas de los controles permanecen bloqueados durante el modo de anotación. Las llamadas explícitas a
request() mantienen su comportamiento existente.
Añade contexto a una selección
Añade oai-annotation-metadata a un objetivo marcado para incluir contexto que puede no
ser visible en la página. Por ejemplo, una fila de contacto ficticia puede incluir una
dirección de correo electrónico para una solicitud de redacción de un correo:
<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>Seleccionar cualquiera de las dos líneas selecciona toda la fila. Los metadatos aparecen con la anotación y la acompañan en la conversación. Incluye solo el contexto que quieras compartir tanto con el usuario como con el modelo. Enviar un correo seguiría requiriendo una herramienta de correo conectada.
Usa un objeto JSON pequeño y plano con estos límites:
- Hasta seis propiedades, con valores de cadena, número finito, booleano o
null. - Claves de hasta 64 caracteres y valores de cadena de hasta 256 caracteres.
- Hasta 2048 bytes para el objeto serializado.
Las claves deben comenzar con una letra ASCII y contener solo letras ASCII, dígitos, espacios, guiones bajos o guiones. Usa un solo espacio entre palabras. No se admiten objetos ni matrices anidados. El navegador ignora los metadatos no válidos.
También puedes proporcionar metadatos a través de request() o del resultado de hitTest
de una superficie.
Abre una anotación desde tu sitio web
Llama a document.oai.annotation.request(target, options) directamente desde una interacción del
usuario, como pulsar un botón. El objetivo puede ser un elemento HTML conectado
dentro del área visible del documento actual o un
Range del DOM. No necesita atributos de
anotación. No se admiten identificadores de objetos virtuales.
Las solicitudes iniciadas por el sitio pueden requerir permiso del usuario; los usuarios pueden volver a habilitar las funciones de anotación bloqueadas en Herramientas del sitio > Funciones de anotación.
Añade este botón junto a la tarjeta de gráfico del ejemplo de selección y ejecuta el script cuando ambos elementos existan:
<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.",
});
});Seleccionar Explicar más solicita al navegador que abra una anotación con la tarjeta seleccionada y un comentario editable. La persona puede editarla, guardarla y enviarla con su mensaje. Abrir una anotación no envía un mensaje a ChatGPT; solo el usuario puede enviarlo.
Mantén la llamada dentro de la interacción activa del usuario. Esperar primero a que termine una solicitud de red puede hacer que se pierda esa interacción.
El segundo argumento opcional admite estos campos:
| Opción | Comportamiento |
|---|---|
mode |
Usa "advanced" para abrir los controles avanzados de un elemento o "default" para usar el comportamiento predeterminado del editor. El valor predeterminado es "default". Los controles personalizados pueden seguir abriéndose con "default"; consulta Valores predeterminados del editor. Los rangos de texto solo admiten "default". |
enterAnnotationMode |
Establécelo en true para entrar en el modo de anotación y permanecer en él después de cancelar o enviar la anotación. |
metadata |
Los metadatos válidos y no vacíos reemplazan los metadatos HTML del objetivo para esta solicitud. Esta opción se ignora para los rangos de texto y cuando el elemento objetivo está dentro de un shadow DOM. |
initialComment |
Proporciona un comentario editable de hasta 240 unidades de código UTF-16. |
request() devuelve un objeto con un booleano accepted. Comprueba
result.accepted, no el objeto de resultado, para saber si el navegador recibió
y validó la solicitud. No confirma que el editor se haya abierto ni que
la persona haya guardado o enviado una anotación.
Mientras se carga el editor, el navegador puede mantener una solicitud aceptada. Puede rechazar otra mientras haya una solicitud pendiente, un editor abierto o ChatGPT esté controlando el navegador.
Solicita una anotación para un rango de texto
Pasa un Range del DOM para solicitar comentarios sobre un pasaje sin cambiar la selección de
texto del navegador. Este ejemplo selecciona el contenido del párrafo; no necesita
un atributo 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>Ejecuta este script cuando ambos elementos existan:
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.",
});
});El rango debe contener texto visible no vacío en el documento actual, con al menos una parte de la selección en el área visible de la página. Puede contener como máximo 20 000 unidades de código UTF-16. No se admiten rangos contraídos, texto compuesto solo por espacios en blanco, texto seleccionado oculto ni rangos en raíces shadow cerradas.
Las solicitudes de rangos de texto usan solo el editor de texto predeterminado. Una solicitud con
mode: "advanced" se rechaza y sus metadatos se ignoran. Mantén el texto
objetivo disponible mientras una solicitud aceptada espera al editor: el navegador
vuelve a comprobar el rango antes de abrirlo. Los contenedores de selección de texto permiten, por separado,
que las personas arrastren el puntero para seleccionar texto durante el modo de anotación.
Elige el modo predeterminado del editor
Para mostrar controles avanzados inmediatamente en las anotaciones abiertas a través de la
interfaz de selección del navegador, añade esta etiqueta al <head> de tu página:
<meta name="oai-annotation-editor-default-mode" content="advanced" />Para las anotaciones abiertas desde tu propia interfaz, pasa { mode: "advanced" } a
request(). Usa mode: "default", u omítelo, para obtener el comportamiento predeterminado del editor.
La configuración meta de la página no anula esta opción de la solicitud. El modo predeterminado
no garantiza un editor solo de comentarios: los controles personalizados con valores iniciales
propuestos, o los controles que omiten currentValue, pueden abrir el editor de controles.
Los controles manuales Ajustar, contraer y Opción-clic solo están disponibles en Codex o en localhost. En los sitios alojados en ChatGPT, los controles personalizados registrados aparecen automáticamente sin un botón Ajustar ni de contracción. El modo avanzado solicitado por la página sigue funcionando allí.
Añade controles personalizados
Usa registerControls() para asociar controles de anotación con uno o varios elementos
DOM. Los controles pueden previsualizar propiedades de la aplicación, como un token de espaciado,
o recopilar elecciones para incluirlas en una solicitud, como el tono de un correo electrónico.
Previsualiza un token de espaciado compartido
Ambas tarjetas de este ejemplo usan la misma propiedad 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>Ejecuta este script después de crear la vista previa:
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);
}Anota cualquiera de las tarjetas y cambia Relleno de la tarjeta (píxeles) de 16 a 24. En los
sitios alojados en ChatGPT, los controles aparecen automáticamente; en Codex o en
localhost, selecciona Ajustar si es necesario. Ambas tarjetas se actualizan. La anotación registra la etiqueta, la referencia
y los valores anteriores y nuevos. Borrar la vista previa restaura el relleno original.
Llama a disposeAnnotationControls() al eliminar el componente.
Recopila una elección sin una vista previa
Usa la fila de contacto del ejemplo de metadatos para ofrecer un tono de correo electrónico:
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",
},
],
});Este control no necesita un manejador de eventos porque no previsualiza un cambio en la
página. Omitir currentValue indica al navegador que incluya el tono seleccionado
incluso si el usuario conserva la opción inicial. Llama a registration?.dispose() al
eliminar la fila.
Para los controles de selección, las funciones de devolución de llamada de vista previa reciben option.value, como
"professional". El historial de anotaciones y ChatGPT reciben el valor visible de
option.label, como "Professional", tanto para las opciones anteriores como para las
seleccionadas. Usa etiquetas que expliquen cada elección; los identificadores internos de value no se
envían como texto de la elección.
Configura los controles y los valores iniciales
Usa controlsMode: "replace" para mostrar solo tus controles para los objetivos
registrados o "extend" para mostrarlos junto a los controles integrados. Un
controlsHeading opcional asigna un nombre al panel. El navegador elimina los espacios en los extremos y acepta de uno a 80
caracteres. Sin un encabezado, el panel muestra la etiqueta HTML del elemento. El
encabezado no se incluye en el contexto enviado a ChatGPT.
Un registro admite hasta 12 controles. Cada uno requiere un type, un
label visible y un identificador callback:
| Tipo | Valor | Campos adicionales |
|---|---|---|
color |
Color hexadecimal, como "#2563eb" |
Ninguno |
range |
Número | min, max y step |
select |
Cadena | options, una matriz de objetos { label, value } |
toggle |
Booleano | Ninguno |
callback es un identificador de cadena, no una función de JavaScript. Haz que sea único
dentro del registro, comiénzalo con una letra ASCII y usa letras ASCII,
dígitos, guiones bajos o guiones. Un reference opcional identifica la propiedad
que se modifica y acompaña a la etiqueta y al valor en la anotación.
Establece currentValue en el valor habitual válido de la propiedad, incluidas las ediciones
sin guardar. No uses el estado de vista previa ni una entrada incompleta como referencia. El navegador
lo usa para los cambios de antes y después y para los restablecimientos. Cuando cambie el estado de la aplicación, usa
registration.update({ controls }) para actualizar los valores currentValue
de los controles. Las actualizaciones afectan a las anotaciones futuras; las anotaciones existentes conservan sus
valores capturados.
Establece defaultValue para sugerir un valor inicial; tiene prioridad sobre
currentValue para el estado inicial del
control. Si omites ambos, el control comienza con blanco para un color, el mínimo
para un rango, la primera opción para una selección o false para un interruptor.
Mantén los valores propuestos separados de los borradores habituales. Si una actualización lanza una excepción o una
solicitud falla o devuelve accepted: false, descarta la propuesta intentada y
restaura los controles anteriores y el estado previo de la propuesta. Conserva las propuestas cuando se acepte una
solicitud: el navegador puede ponerla en cola antes de capturar los controles.
Valida los controles y los registros
El navegador valida los registros y las actualizaciones según estos límites:
| Campo o recurso | Restricción |
|---|---|
| Controles | Hasta 12 por registro, con identificadores callback únicos. Usa solo los campos definidos para el tipo de control. |
| Etiquetas y encabezados | Las etiquetas de controles, las etiquetas de opciones y controlsHeading no deben estar vacíos después de eliminar los espacios en los extremos y deben tener como máximo 80 unidades de código UTF-16. El texto de los controles no puede contener caracteres de control ni caracteres que cambien la dirección del texto. |
| Identificadores | callback tiene como máximo 80 unidades de código UTF-16 después de eliminar los espacios en los extremos y usa el formato descrito anteriormente. reference tiene entre 1 y 80 caracteres y admite letras ASCII, dígitos y _ . / : @ $ # -, sin espacios. |
| Opciones de selección | De 1 a 12 opciones con cadenas value distintas de como máximo 512 unidades de código UTF-16. Los valores proporcionados de currentValue y defaultValue deben coincidir con el valor de una opción. |
| Valores de rango | min, max, currentValue y defaultValue deben ser números finitos entre −10 000 y 10 000. Se requiere min < max, un step de 0.001 a 10 000 que no sea mayor que max - min y valores iniciales dentro del rango. Los valores iniciales no tienen que ajustarse a step. |
| Colores e interruptores | Los colores deben usar 3, 4, 6 u 8 dígitos hexadecimales después de #. Los valores de los interruptores deben ser true o false. |
| Objetivos | De 1 a 128 entradas de objetivos al registrar, todos ellos elementos del documento actual. Las actualizaciones pueden pasar targets: [] para desvincularlos. Cada documento admite hasta 64 registros de controles y 1024 asociaciones entre registros y objetivos. |
| Controles serializados | La carga JSON que contiene controls, controlsHeading y controlsMode debe caber en 16 384 unidades de código UTF-16. Usa datos que puedan codificarse como JSON, sin funciones, símbolos ni enteros grandes. |
registerControls() y registration.update() pueden lanzar excepciones de forma síncrona por
definiciones u objetivos no válidos o por superar los límites. Una actualización después de dispose()
también lanza una excepción. Una actualización rechazada conserva el registro anterior, incluidos
sus controles y objetivos. Gestiona los fallos en el punto de llamada, mantén utilizable la
edición habitual y reutiliza o libera los registros que administras para mantenerte dentro de los
límites.
Gestiona las vistas previas y los restablecimientos
El evento oaiannotationcontrolchange se propaga desde el elemento seleccionado. Su
detail contiene callback, value y action:
| Acción | Aplica el valor proporcionado para |
|---|---|
preview |
Mostrar el cambio solicitado. |
preview-original |
Mostrar temporalmente el estado original para compararlo. |
reset |
Restaurar el estado original cuando se borra la vista previa. |
Aplica el valor proporcionado para cada acción, como hace el ejemplo de espaciado. Haz que los manejadores sean reversibles y seguros al llamarlos repetidamente. Los eventos de vista previa no solicitan un cambio permanente; guarda los cambios mediante el flujo de guardado habitual de tu aplicación. Mantén separados los borradores habituales, las entradas incompletas y las vistas previas para que las ediciones no relacionadas no sobrescriban una vista previa. Cuando alguien edite la misma configuración, reemplaza su vista previa y actualiza el valor de referencia para las anotaciones futuras.
Conserva el identificador de registro para las actualizaciones y la limpieza. update() acepta cualquier
combinación de targets, controls, controlsHeading y controlsMode.
Los campos omitidos conservan sus valores anteriores. Por ejemplo, usa
registration.update({ targets: newElement }) al reemplazar el elemento DOM de un
componente. Las colecciones de objetivos capturan los elementos existentes y no rastrean futuras
coincidencias del selector.
Pasa targets: [] para desvincular el registro o controls: [] para borrar sus
controles. Llama a dispose() y elimina los detectores de eventos al eliminar la
integración. La limpieza también debe restaurar la representación habitual: dispose() solo
elimina el registro de controles y no deshace los cambios de tu vista previa. El ejemplo de espaciado
elimina su sobrescritura en línea para restaurar el valor CSS original. Ambos métodos
retornan de forma síncrona sin un valor. Las actualizaciones afectan a
las selecciones futuras; las anotaciones guardadas conservan sus controles y su objetivo capturados.
Los eventos de control se dirigen al elemento capturado por la anotación. Actualizar los objetivos de un registro no redirige las anotaciones existentes. Los eventos de restablecimiento pueden seguir activándose en un elemento eliminado, por lo que un detector en su antiguo elemento padre no los recibirá.
Permite seleccionar objetos del lienzo
Una superficie de anotación permite que tu aplicación identifique objetos individuales dibujados
dentro de un lienzo. Registra el elemento anfitrión con registerSurface() y proporciona
una función hitTest que devuelva un objeto con un id estable o null para
el espacio vacío.
El anfitrión debe ser un elemento HTML conectado fuera del shadow DOM en un documento seguro de nivel superior. No se admite el registro de superficies dentro de un iframe.
Este ejemplo dibuja una barra de ingresos y permite seleccionarla. Coloca el script después del lienzo:
<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"
);
},
});Al pasar el puntero sobre la barra en el modo de anotación, esta se resalta. Al seleccionarla, se abre una anotación con el nombre del objeto, los metadatos y una captura de pantalla de la selección.
Mantén estables los identificadores dentro de una superficie. El name opcional es visible para el usuario;
role proporciona una breve descripción semántica. El rect opcional usa píxeles CSS
relativos al área visible de la página, que coinciden con clientX y clientY. Convierte las coordenadas de la escena,
incluidos la escala, el desplazamiento y el zoom.
hitTest puede devolver una promesa y recibe un AbortSignal como signal para
cancelar el trabajo reemplazado. El navegador espera 250 milisegundos antes de recurrir
a la selección del DOM. Los errores y los resultados no válidos también hacen que se recurra a ella. Devuelve
null explícitamente cuando la prueba de detección se complete correctamente sin encontrar ningún objeto.
El trabajo cancelado debe resolver o rechazar su promesa de todos modos. El navegador permite
solo una función de devolución de llamada hitTest en curso y mantiene ese espacio ocupado hasta que la promesa
se resuelva o se rechace, incluso después de una cancelación o de que se agote el tiempo de espera. Si un worker gestiona la selección,
resuelve o rechaza la promesa pendiente cuando se interrumpa su trabajo; descartar la respuesta de un worker
cancelado puede bloquear las selecciones posteriores en el lienzo.
Usa la función de devolución de llamada opcional renderSelection para proporcionar indicaciones específicas de la aplicación.
Borra las indicaciones cuando ambos identificadores sean null. Llama a surface?.invalidate() después de
mover objetos o cambiar el zoom, y a surface?.dispose() al eliminar la
integración. Ambas retornan de forma síncrona sin un valor.
Añade controles a los objetos del lienzo
Registra controles personalizados en el elemento DOM de la superficie. Los objetos dibujados, llamados objetos virtuales en la API, usan tus controles personalizados; los controles integrados de CSS y texto no se aplican a ellos.
Para varios objetos, usa renderSelection para actualizar los controles del anfitrión cuando
cambie selectedId, antes de que el navegador capture la anotación. Los eventos de
control incluyen detail.virtualTarget: { surfaceId, targetId }. Dirige cada evento
usando esa identidad capturada y su callback, en lugar de la selección
actual. targetId coincide con el id de hitTest; surfaceId identifica el
registro de superficie del navegador. Los eventos DOM habituales omiten virtualTarget.
Los eventos de vista previa, comparación y restablecimiento conservan la identidad del objeto original después de seleccionar otro objeto. Reabrir una anotación guardada no vuelve a ejecutar la prueba de detección para reemplazar el objeto o los metadatos capturados.
Controla el modo de anotación
Usa toggle() para solicitar un cambio de modo, isActive() para leer el estado confirmado
y el evento oaiannotationmodechange del documento para mantener tu interfaz sincronizada.
Detecta la disponibilidad de ambos métodos para los navegadores antiguos o no compatibles. Añade este botón
y ejecuta el script cuando exista:
<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() invierte el modo. Pasa true para asegurarte de que esté activado o false para asegurarte
de que esté desactivado. Las solicitudes repetidas con el mismo booleano son idempotentes. Pasar
true conserva un editor activo o una activación pendiente; false cancela
la activación pendiente y usa el flujo de salida normal.
Llama a toggle() desde una interacción activa del usuario. Su resultado síncrono { accepted }
acusa recibo de la solicitud, pero no confirma un cambio de modo. Las comprobaciones de
compatibilidad del navegador aún pueden impedir el cambio. Lee el estado desde isActive()
y el evento, como hace el ejemplo.
isActive() no necesita un gesto del usuario. El navegador lo actualiza antes de emitir
oaiannotationmodechange, cuyo event.detail.active es un booleano. El navegador
no envía un evento inicial ni uno duplicado si una solicitud forzada no produce ningún cambio, así que inicializa tu interfaz
con el método de lectura. Si se revoca el acceso mientras está activo, un evento final informa de
active: false y un método de lectura conservado devuelve false.
El modo de anotación captura los clics de la página. Mantén pulsada Espacio para usar los controles de la página,
incluido tu botón de salida, o sal a través de la interfaz de anotación del navegador.
Mantener pulsada solo Espacio deja isActive() en true. La API no admite un
oai-annotation-ignore ni otro atributo que permita que los clics pasen a través.
Al salir, se cierra el editor y se conservan las anotaciones guardadas sin enviarlas
ni moverlas al cuadro de redacción. Llama a cleanupAnnotationButton() al
eliminar el componente. La referencia capturada a la API permite que la limpieza elimine
los detectores incluso si el espacio de nombres ha desaparecido.
Prueba tu integración
Abre tu sitio web en el navegador integrado de la aplicación de escritorio y prueba las funciones que añadiste:
- Entra en el modo de anotación y selecciona objetos y texto. Comprueba que los resaltados, nombres, rangos y metadatos coincidan con los objetivos previstos.
- Abre una anotación desde el botón de tu sitio. Comprueba el elemento seleccionado o el rango de texto, el comentario inicial y el modo del editor.
- Cambia un control personalizado, compara con el original y borra la vista previa. Comprueba que tu aplicación restaure el estado original.
- Para el contenido del lienzo, prueba el espacio vacío, el cambio de tamaño y los cambios de escena. Cambia de objeto y confirma que los eventos de control sigan actualizando el objetivo capturado. Cancela una prueba de detección asíncrona y confirma que las selecciones posteriores sigan funcionando.
- Guarda, vuelve a abrir y edita una anotación desde la vista previa del archivo adjunto en el cuadro de redacción. Comprueba que conserve sus controles y su objetivo, y que al eliminarla se borre cualquier vista previa.
- Envía una anotación con un mensaje. Confirma que ChatGPT reciba el
contenido seleccionado, los metadatos y los valores solicitados, incluidas las etiquetas visibles de las opciones de
selección y las elecciones sin cambios en los controles que omiten
currentValue. - Abre el sitio en un navegador sin la API y verifica que las interacciones normales sigan funcionando.
Para exponer acciones que ChatGPT pueda realizar en tu sitio web, añade Herramientas del sitio (WebMCP). Las anotaciones incorporan la selección y los comentarios de la persona a la conversación; las herramientas del sitio permiten que el agente actúe sobre ese contexto a través de las capacidades existentes de tu aplicación.