Extensibilidade das anotações

A Browser Annotation API permite que o seu site personalize o que as pessoas selecionam, o contexto que acompanha os seus comentários e os controlos que utilizam para pré-visualizar alterações antes de enviar uma anotação para o ChatGPT.

As anotações do navegador funcionam no seu site sem quaisquer alterações ao código. As pessoas podem selecionar parte de uma página, adicionar um comentário e enviá-lo no Context para o Codex ou o ChatGPT Work.

Enquanto programador, pode utilizar a Browser Annotation API para fornecer contexto ou controlos específicos da sua aplicação. Por exemplo, pode anexar pré-visualizações de variantes de componentes numa pré-visualização de um sistema de design, para que os programadores saibam como atualizar os componentes num site.

Para obter ajuda para compreender a Browser Annotation API ou adicionar suporte a anotações ao seu site, instale o plugin Annotations Extensibility.

Instalar o plugin Annotations Extensibility

Experimente

Veja a Browser Annotation API em ação neste guia.

  1. Abra esta página no navegador integrado do ChatGPT.
  2. Experimente abrir o pedido sugerido que aparecerá neste cartão.
  3. Entre no modo de anotação e, em seguida, selecione a tabela abaixo ou um exemplo de código para alternar entre disposições e temas predefinidos.
  4. Pode continuar a anotar qualquer item da página e a observar o comportamento predefinido das anotações.

Abra esta página no navegador integrado do ChatGPT para experimentar as anotações.

Abrir no navegador do ChatGPT

Escolher o que personalizar

Comece pela integração adequada ao seu site:

Objetivo Integração
Tornar um cartão ou outro grupo de elementos selecionável como um único objeto Alvos de seleção
Permitir que as pessoas selecionem uma expressão ou frase Contentores de seleção de texto
Incluir contexto adicional numa seleção Metadados de seleção
Abrir uma anotação a partir do seu próprio botão com um comentário sugerido Pedidos de anotação
Pedir comentários sobre uma passagem exata a partir da sua própria interface Pedidos para intervalos de texto
Mostrar controlos avançados quando uma anotação é aberta Predefinições do editor
Pré-visualizar propriedades da aplicação ou recolher escolhas Controlos personalizados
Selecionar objetos individuais desenhados num canvas Superfícies de anotação
Ativar ou desativar o modo de anotação a partir do seu site Controlos do modo de anotação

Este guia destina-se à versão DevDay 2026 da aplicação de ambiente de trabalho do ChatGPT e posteriores. A API JavaScript está disponível através de document.oai.annotation no navegador integrado da aplicação, em páginas seguras de nível superior, como HTTPS ou localhost. Quando ativada, o navegador instala-a antes de executar os scripts da página. Detete a disponibilidade de cada método para suportar navegadores mais antigos ou não compatíveis e, em seguida, inicialize a sua integração assim que os respetivos elementos DOM existirem. Não é necessário qualquer evento de prontidão nem consulta periódica.

Os métodos da API devolvem resultados de forma síncrona e os identificadores de registo ficam prontos a utilizar imediatamente. O navegador pode concluir o carregamento do editor de anotações depois disso. A função de retorno hitTest de uma superfície pode devolver uma promessa.

Personalizar alvos de seleção

Por predefinição, o modo de anotação seleciona elementos do DOM da página, dando preferência a alvos como texto, imagens e controlos. Para tornar um objeto maior selecionável, marque a região que o contém com oai-annotation-container e os seus descendentes selecionáveis com oai-annotatable.

Este exemplo torna um cartão de gráfico selecionável como um único 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>

Apontar para qualquer ponto do cartão realça o cartão inteiro. O valor opcional oai-annotatable atribui ao objeto um nome apresentado ao utilizador e ao modelo. Escolha nomes que distingam objetos próximos ou omita o valor.

O contentor define onde se aplicam estas regras de seleção. Um atributo oai-annotatable, por si só, não altera o comportamento da seleção. Dentro de um contentor, o navegador seleciona o alvo marcado mais próximo que contém o elemento sob o ponteiro. Os contentores aninhados utilizam o contentor mais próximo. As áreas não marcadas dentro de um contentor criam uma anotação de página Web; as áreas fora de todos os contentores mantêm o comportamento predefinido.

Ativar a seleção de texto

Adicione oai-annotation-container-text a uma região para permitir que as pessoas arrastem para selecionar texto durante o modo de anotação. O atributo não necessita de um 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>

Ao soltar uma seleção não vazia, abre-se o editor de anotações de texto com o intervalo selecionado e o respetivo contexto. Clicar sem selecionar texto não cria uma anotação. Escape ou um gesto cancelado cancela a seleção.

O contentor de seleção de texto ou do DOM mais próximo determina como começa um gesto. Os contentores de texto não utilizam marcadores oai-annotatable para selecionar elementos. Aninhe um oai-annotation-container para restaurar a seleção de elementos ou um contentor de texto para restaurar a seleção de texto. Se ambos os atributos estiverem no mesmo elemento, a seleção de texto tem precedência.

A seleção de texto segue as regras normais de seleção da página e pode estender-se para além do contentor inicial. Um contentor não limita o intervalo nem ativa a seleção dentro de um iframe. Os campos de texto continuam a ser selecionáveis, mas os cliques normais na página e as ações nativas dos controlos permanecem bloqueados durante o modo de anotação. As chamadas explícitas a request() mantêm o seu comportamento existente.

Adicionar contexto a uma seleção

Adicione oai-annotation-metadata a um alvo marcado para incluir contexto que pode não estar visível na página. Por exemplo, uma linha de contacto fictícia pode incluir um endereço de e-mail para um pedido de redação de um e-mail:

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

Selecionar qualquer uma das linhas seleciona a linha de contacto inteira. Os metadados aparecem com a anotação e acompanham-na na conversa. Inclua apenas contexto que pretenda partilhar tanto com o utilizador como com o modelo. O envio de um e-mail continuaria a exigir uma ferramenta de e-mail ligada.

Utilize um objeto JSON pequeno e sem estruturas aninhadas, com estes limites:

  • Até seis propriedades, com valores de cadeia de caracteres, número finito, booleano ou null.
  • Chaves com até 64 caracteres e valores de cadeia de caracteres com até 256 caracteres.
  • Até 2 048 bytes para o objeto serializado.

As chaves têm de começar por uma letra ASCII e conter apenas letras ASCII, algarismos, espaços, sublinhados ou hífenes. Utilize espaços simples entre palavras. Não são suportados objetos aninhados nem matrizes. O navegador ignora metadados inválidos.

Também pode fornecer metadados através de request() ou do resultado hitTest de uma superfície.

Abrir uma anotação a partir do seu site

Chame document.oai.annotation.request(target, options) diretamente a partir de uma interação do utilizador, como premir um botão. O alvo pode ser um elemento HTML ligado ao DOM dentro da área visível do documento atual ou um Range do DOM. Não necessita de atributos de anotação. Os IDs de objetos virtuais não são suportados.

Os pedidos iniciados pelo site podem exigir permissão do utilizador; os utilizadores podem reativar funcionalidades de anotação bloqueadas em Ferramentas do site > Funcionalidades de anotação.

Adicione este botão junto ao cartão de gráfico do exemplo de seleção e execute o script depois de ambos os elementos existirem:

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

Selecionar Explicar melhor pede ao navegador que abra uma anotação com o cartão selecionado e um comentário editável. A pessoa pode editá-lo, guardá-lo e enviá-lo com a sua mensagem. Abrir uma anotação não envia uma mensagem para o ChatGPT; apenas o utilizador a pode enviar.

Mantenha a chamada dentro da interação ativa do utilizador. Aguardar primeiro um pedido de rede pode fazer com que essa interação se perca.

O segundo argumento opcional suporta estes campos:

Opção Comportamento
mode Utilize "advanced" para abrir controlos avançados para um elemento ou "default" para utilizar o comportamento predefinido do editor. A predefinição é "default". Os controlos personalizados podem continuar a abrir com "default"; consulte Predefinições do editor. Os intervalos de texto suportam apenas "default".
enterAnnotationMode Defina como true para entrar no modo de anotação e permanecer nele após cancelar ou enviar a anotação.
metadata Metadados válidos e não vazios substituem os metadados HTML do alvo neste pedido. Esta opção é ignorada para intervalos de texto e quando o elemento alvo está dentro de um shadow DOM.
initialComment Fornece um comentário editável, com até 240 unidades de código UTF-16.

request() devolve um objeto com um booleano accepted. Verifique result.accepted, e não o objeto de resultado, para saber se o navegador recebeu e validou o pedido. Não confirma que o editor abriu nem que a pessoa guardou ou enviou uma anotação.

Enquanto o editor carrega, o navegador pode manter um pedido aceite em espera. Pode recusar outro enquanto um pedido estiver pendente, um editor estiver aberto ou o ChatGPT estiver a controlar o navegador.

Pedir uma anotação para um intervalo de texto

Passe um Range do DOM para pedir comentários sobre uma passagem sem alterar a seleção de texto do navegador. Este exemplo seleciona o conteúdo do parágrafo; não necessita de um 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>

Execute este script depois de ambos os elementos existirem:

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

O intervalo tem de conter texto visível não vazio no documento atual, com pelo menos parte da seleção na área visível da página. Pode conter, no máximo, 20 000 unidades de código UTF-16. Não são suportados intervalos recolhidos, texto composto apenas por espaços em branco, texto selecionado oculto nem intervalos em raízes shadow fechadas.

Os pedidos para intervalos de texto utilizam apenas o editor de texto predefinido. Um pedido com mode: "advanced" é rejeitado e os metadados do pedido são ignorados. Mantenha o texto alvo disponível enquanto um pedido aceite aguarda pelo editor: o navegador volta a verificar o intervalo antes de o abrir. Os contentores de seleção de texto permitem, separadamente, que as pessoas arrastem para selecionar texto durante o modo de anotação.

Escolher o modo predefinido do editor

Para mostrar controlos avançados imediatamente nas anotações abertas através da interface de seleção do navegador, adicione esta etiqueta ao <head> da sua página:

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

Para anotações abertas a partir da sua própria interface, passe { mode: "advanced" } a request(). Utilize mode: "default", ou omita-o, para obter o comportamento predefinido do editor. A definição meta da página não substitui esta opção do pedido. O modo predefinido não garante um editor apenas de comentários: os controlos personalizados com valores iniciais propostos, ou os controlos que omitem currentValue, podem abrir o editor de controlos.

Os controlos manuais Ajustar, de recolher e de Option-clique estão disponíveis apenas no Codex ou em localhost. Nos sites alojados no ChatGPT, os controlos personalizados registados aparecem automaticamente sem um botão Ajustar ou de recolher. O modo avançado pedido pela página continua a funcionar nesses sites.

Adicionar controlos personalizados

Utilize registerControls() para associar controlos de anotação a um ou mais elementos DOM. Os controlos podem pré-visualizar propriedades da aplicação, como um token de espaçamento, ou recolher escolhas a incluir num pedido, como o tom de um e-mail.

Pré-visualizar um token de espaçamento partilhado

Ambos os cartões deste exemplo utilizam a mesma propriedade 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>

Execute este script depois de criar a pré-visualização:

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

Anote qualquer um dos cartões e altere Espaçamento interior do cartão (píxeis) de 16 para 24. Nos sites alojados no ChatGPT, os controlos aparecem automaticamente; no Codex ou em localhost, selecione Ajustar, se necessário. Ambos os cartões são atualizados. A anotação regista o rótulo, a referência e os valores antigos e novos. Limpar a pré-visualização restaura o espaçamento interior original. Chame disposeAnnotationControls() ao remover o componente.

Recolher uma escolha sem pré-visualização

Utilize a linha de contacto do exemplo de metadados para disponibilizar um tom de e-mail:

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 controlo não necessita de um processador de eventos porque não pré-visualiza uma alteração na página. Omitir currentValue indica ao navegador que deve incluir o tom selecionado mesmo que o utilizador mantenha a opção inicial. Chame registration?.dispose() ao remover a linha.

Para controlos de seleção, as funções de retorno de pré-visualização recebem option.value, como "professional". O histórico de anotações e o ChatGPT recebem o option.label visível, como "Professional", tanto para as opções anteriores como para as selecionadas. Utilize rótulos que expliquem cada escolha; os IDs internos em value não são enviados como texto da escolha.

Configurar controlos e valores iniciais

Utilize controlsMode: "replace" para mostrar apenas os seus controlos para os alvos registados ou "extend" para os mostrar juntamente com os controlos integrados. Um controlsHeading opcional atribui um nome ao painel. O navegador remove os espaços nas extremidades e aceita de um a 80 caracteres. Sem um título, o painel mostra a etiqueta HTML do elemento. O título não é incluído no contexto enviado para o ChatGPT.

Um registo suporta até 12 controlos. Cada um exige um type, um label visível e um identificador callback:

Tipo Valor Campos adicionais
color Cor hexadecimal, como "#2563eb" Nenhum
range Número min, max e step
select Cadeia de caracteres options, uma matriz de objetos { label, value }
toggle booleano Nenhum

callback é um identificador em forma de cadeia de caracteres, não uma função JavaScript. Torne-o único dentro do registo, comece-o por uma letra ASCII e utilize letras ASCII, algarismos, sublinhados ou hífenes. Um reference opcional identifica a propriedade que está a ser alterada e acompanha o rótulo e o valor na anotação.

Defina currentValue como o valor normal válido da propriedade, incluindo alterações não guardadas. Não utilize o estado de pré-visualização nem dados de entrada incompletos como referência. O navegador utiliza-o para alterações de antes e depois e reposições. Quando o estado da aplicação mudar, utilize registration.update({ controls }) para atualizar os valores currentValue dos controlos. As atualizações afetam anotações futuras; as anotações existentes mantêm os valores capturados.

Defina defaultValue para sugerir um valor inicial; tem precedência sobre currentValue para o estado inicial do controlo. Se omitir ambos, o controlo começa com branco para uma cor, o mínimo para um intervalo, a primeira opção para uma seleção ou false para um interruptor.

Mantenha os valores propostos separados dos rascunhos normais. Se uma atualização lançar uma exceção ou um pedido falhar ou devolver accepted: false, descarte a proposta tentada e restaure o estado anterior dos controlos e da proposta. Mantenha as propostas quando um pedido for aceite: o navegador pode colocá-lo em fila antes de capturar os controlos.

Validar controlos e registos

O navegador valida os registos e as atualizações de acordo com estes limites:

Campo ou recurso Restrição
Controlos Até 12 por registo, com identificadores callback únicos. Utilize apenas os campos definidos para o tipo de controlo.
Rótulos e títulos Os rótulos dos controlos, os rótulos das opções e controlsHeading têm de ser não vazios após a remoção dos espaços nas extremidades e ter, no máximo, 80 unidades de código UTF-16. O texto dos controlos não pode conter caracteres de controlo nem caracteres que alterem a direção do texto.
Identificadores callback tem, no máximo, 80 unidades de código UTF-16 após a remoção dos espaços nas extremidades, utilizando o formato descrito acima. reference tem de 1 a 80 caracteres e permite letras ASCII, algarismos e _ . / : @ $ # -, sem espaços.
Opções de seleção De 1 a 12 opções com cadeias de caracteres value distintas, com, no máximo, 512 unidades de código UTF-16. Os valores fornecidos de currentValue e defaultValue têm de corresponder ao valor de uma opção.
Valores de intervalo min, max, currentValue e defaultValue têm de ser números finitos entre −10 000 e 10 000. É obrigatório min < max, um step de 0.001 a 10 000 que não seja superior a max - min e valores iniciais dentro do intervalo. Os valores iniciais não têm de estar alinhados com step.
Cores e interruptores As cores têm de utilizar 3, 4, 6 ou 8 algarismos hexadecimais após #. Os valores dos interruptores têm de ser true ou false.
Alvos De 1 a 128 entradas de alvos no momento do registo, todas elementos do documento atual. As atualizações podem passar targets: [] para desassociar. Cada documento suporta até 64 registos de controlos e 1 024 associações entre registos e alvos.
Controlos serializados O conteúdo JSON que contém controls, controlsHeading e controlsMode tem de caber em 16 384 unidades de código UTF-16. Utilize dados que possam ser codificados como JSON, sem funções, símbolos ou inteiros grandes.

registerControls() e registration.update() podem lançar exceções de forma síncrona devido a definições ou alvos inválidos, ou a limites excedidos. Uma atualização após dispose() também lança uma exceção. Uma atualização rejeitada preserva o registo anterior, incluindo os seus controlos e alvos. Trate as falhas no local da chamada, mantenha a edição normal utilizável e reutilize ou elimine os registos sob a sua responsabilidade para respeitar os limites.

Tratar pré-visualizações e reposições

O evento oaiannotationcontrolchange propaga-se a partir do elemento selecionado. O seu detail contém callback, value e action:

Ação Aplicar o valor fornecido para
preview Mostrar a alteração pedida.
preview-original Mostrar temporariamente o estado original para comparação.
reset Restaurar o estado original quando a pré-visualização é limpa.

Aplique o valor fornecido em todas as ações, como no exemplo de espaçamento. Torne os processadores reversíveis e seguros para chamadas repetidas. Os eventos de pré-visualização não pedem uma alteração permanente; guarde através do fluxo normal de gravação da sua aplicação. Mantenha os rascunhos normais, os dados de entrada incompletos e as pré-visualizações separados, para que alterações não relacionadas não substituam uma pré-visualização. Quando alguém editar a mesma definição, substitua a respetiva pré-visualização e atualize o valor de referência para anotações futuras.

Mantenha o identificador de registo para atualizações e limpeza. update() aceita qualquer combinação de targets, controls, controlsHeading e controlsMode. Os campos omitidos mantêm os valores anteriores. Por exemplo, utilize registration.update({ targets: newElement }) ao substituir o elemento DOM de um componente. As coleções de alvos capturam elementos existentes e não acompanham futuras correspondências dos seletores.

Passe targets: [] para desassociar o registo ou controls: [] para limpar os seus controlos. Chame dispose() e remova os ouvintes de eventos ao remover a integração. A limpeza também tem de restaurar a apresentação normal: dispose() apenas remove o registo dos controlos e não anula as alterações da pré-visualização. O exemplo de espaçamento remove a sua substituição inline para restaurar o valor CSS original. Ambos os métodos terminam de forma síncrona sem devolver um valor. As atualizações afetam seleções futuras; as anotações guardadas mantêm os controlos e o alvo capturados.

Os eventos dos controlos têm como alvo o elemento capturado pela anotação. Atualizar os alvos de um registo não redireciona as anotações existentes. Os eventos de reposição podem continuar a ser emitidos num elemento removido, pelo que um ouvinte no seu antigo elemento ascendente não os receberá.

Tornar objetos de canvas selecionáveis

Uma superfície de anotação permite que a sua aplicação identifique objetos individuais desenhados dentro de um canvas. Registe o elemento anfitrião com registerSurface() e forneça uma função hitTest que devolva um objeto com um id estável ou null para espaço vazio.

O anfitrião tem de ser um elemento HTML ligado ao DOM, fora de shadow DOM, num documento seguro de nível superior. O registo de superfícies não é suportado dentro de um iframe.

Este exemplo desenha uma barra de receitas e torna-a selecionável. Coloque o script depois do 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"
    );
  },
});

Passar o ponteiro sobre a barra no modo de anotação realça-a. Selecioná-la abre uma anotação com o nome do objeto, os metadados e uma captura de ecrã da seleção.

Mantenha os IDs estáveis dentro de uma superfície. O name opcional é visível para o utilizador; role fornece uma breve descrição semântica. O rect opcional utiliza píxeis CSS relativos à área visível da página, correspondendo a clientX e clientY. Converta a partir das coordenadas da cena, incluindo escala, deslocamento e zoom.

hitTest pode devolver uma promessa e recebe um AbortSignal como signal para cancelar trabalho substituído. O navegador permite 250 milissegundos antes de recorrer à seleção do DOM. Os erros e os resultados inválidos também levam a esse recurso. Devolva null explicitamente para um teste de deteção bem-sucedido sem qualquer objeto.

O trabalho cancelado tem, ainda assim, de resolver ou rejeitar a sua promessa. O navegador permite apenas uma função de retorno hitTest em curso e mantém essa posição ocupada até a promessa ficar resolvida ou rejeitada, mesmo após um cancelamento ou tempo limite. Se um worker tratar da seleção, resolva ou rejeite a promessa pendente quando o seu trabalho for interrompido; descartar a resposta de um worker cancelado pode bloquear seleções subsequentes no canvas.

Utilize a função de retorno opcional renderSelection para fornecer indicações específicas da aplicação. Limpe as indicações quando ambos os IDs forem null. Chame surface?.invalidate() depois de mover objetos ou alterar o zoom e surface?.dispose() ao remover a integração. Ambos terminam de forma síncrona sem devolver um valor.

Adicionar controlos a objetos de canvas

Registe controlos personalizados no elemento DOM da superfície. Os objetos desenhados, designados objetos virtuais na API, utilizam os seus controlos personalizados; os controlos integrados de CSS e texto não se lhes aplicam.

Para vários objetos, utilize renderSelection para atualizar os controlos do anfitrião quando selectedId mudar, antes de o navegador capturar a anotação. Os eventos dos controlos incluem detail.virtualTarget: { surfaceId, targetId }. Encaminhe cada evento utilizando essa identidade capturada e o respetivo callback, em vez da seleção atual. targetId corresponde ao id de hitTest; surfaceId identifica o registo da superfície no navegador. Os eventos normais do DOM omitem virtualTarget.

Os eventos de pré-visualização, comparação e reposição mantêm a identidade do objeto original após a seleção de outro objeto. Reabrir uma anotação guardada não volta a executar o teste de deteção para substituir o objeto ou os metadados capturados.

Controlar o modo de anotação

Utilize toggle() para pedir uma alteração de modo, isActive() para ler o estado confirmado e o evento oaiannotationmodechange do documento para manter a sua interface sincronizada. Detete a disponibilidade de ambos os métodos para navegadores mais antigos ou não compatíveis. Adicione este botão e execute o script depois de o botão existir:

<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() inverte o modo. Passe true para garantir que está ativado ou false para garantir que está desativado. Pedidos repetidos com o mesmo booleano são idempotentes. Passar true preserva um editor ativo ou uma ativação pendente; false cancela a ativação pendente e utiliza o fluxo normal de saída.

Chame toggle() a partir de uma interação ativa do utilizador. O seu resultado síncrono { accepted } confirma a receção do pedido, não uma alteração de modo confirmada. As verificações de elegibilidade do navegador ainda podem impedir a alteração. Leia o estado a partir de isActive() e do evento, como no exemplo.

isActive() não necessita de um gesto do utilizador. O navegador atualiza-o antes de emitir oaiannotationmodechange, cujo event.detail.active é um booleano. O navegador não envia um evento inicial nem um duplicado para uma operação forçada sem efeito, pelo que deve inicializar a sua interface a partir do método de leitura. Se o acesso for revogado enquanto estiver ativo, um evento final comunica active: false e um método de leitura mantido devolve false.

O modo de anotação captura os cliques na página. Mantenha Espaço premido para utilizar os controlos da página, incluindo o botão de saída, ou saia através da interface de anotações do navegador. Manter apenas Espaço premido deixa isActive() como verdadeiro. A API não suporta um oai-annotation-ignore nem outro atributo que permita a passagem dos cliques.

Sair fecha o editor e preserva as anotações guardadas sem as enviar nem as mover para o compositor. Chame cleanupAnnotationButton() ao remover o componente. A referência capturada da API permite que a limpeza remova ouvintes mesmo que o espaço de nomes tenha desaparecido.

Testar a sua integração

Abra o seu site no navegador integrado da aplicação de ambiente de trabalho e teste as funcionalidades que adicionou:

  1. Entre no modo de anotação e selecione objetos e texto. Verifique se os realces, nomes, intervalos e metadados correspondem aos alvos pretendidos.
  2. Abra uma anotação a partir do botão do seu site. Verifique o elemento selecionado ou intervalo de texto, o comentário inicial e o modo do editor.
  3. Altere um controlo personalizado, compare com o original e limpe a pré-visualização. Verifique se a sua aplicação restaura o estado original.
  4. Para conteúdo de canvas, teste o espaço vazio, o redimensionamento e as alterações da cena. Alterne entre objetos e confirme que os eventos dos controlos continuam a atualizar o alvo capturado. Cancele um teste de deteção assíncrono e confirme que as seleções posteriores continuam a funcionar.
  5. Guarde, reabra e edite uma anotação a partir da pré-visualização de anexos do compositor. Verifique se mantém os seus controlos e o alvo e se a sua remoção limpa qualquer pré-visualização.
  6. Envie uma anotação com uma mensagem. Confirme que o ChatGPT recebe o conteúdo selecionado, os metadados e os valores pedidos, incluindo os rótulos visíveis das opções de seleção e as escolhas inalteradas nos controlos que omitem currentValue.
  7. Abra o site num navegador sem a API e verifique se as interações normais continuam a funcionar.

Para disponibilizar ações que o ChatGPT possa executar no seu site, adicione Ferramentas do site (WebMCP). As anotações trazem a seleção da pessoa e os seus comentários para a conversa; as ferramentas do site permitem que o agente atue sobre esse contexto através das capacidades existentes da sua aplicação.