omrehbein/ace-editor

Use quando um agente que automatiza uma página web precisar encontrar, ler, substituir ou validar texto dentro de um editor de código Ace (elementos com a classe `ace_editor`) — editores de código/JSON/YAML/SQL embutidos em consoles de nuvem (ex. editores de políticas AWS), Cloudscape/awsui, IDEs estilo Cloud9, react-ace, painéis de administração. Use também quando o texto digitado em um editor de código web sair corrompido (aspas/chaves extras ou faltantes, indentação duplicada), quando apenas parte de um documento estiver visível no DOM ou em uma captura de tela, ou quando for necessário obter os erros de sintaxe do editor.

O que é ace-editor?

Esta skill resolve o problema comum de automação de editores de código Ace (popular em plataformas como AWS, Cloud9 e diversos painéis administrativos). O Ace renderiza apenas as linhas visíveis e recebe entrada de teclado através de uma `textarea` oculta que fecha automaticamente aspas/chaves e auto-indenta novas linhas, o que torna a digitação direta imprevisível e propensa a erros. A skill fornece um conjunto robusto de scripts JavaScript para: localizar dinamicamente todos os editores Ace na página (incluindo dentro de shadow roots e iframes de mesma origem), ler o conteúdo completo do editor, substituir o documento inteiro de uma vez, fazer edições direcionadas (busca/substituição com suporte a expressões regulares), e um método de fallback que cola o texto na `textarea` oculta quando o objeto editor não está acessível (ex. scripts de extensão em mundo isolado). A documentação cobre todos os casos extremos importantes: detectar editores somente leitura, usar a API corretamente (sem chamar `ace.edit()` em elementos que já possuem um editor), preservar o estado de undo, e desabilitar comportamentos intrusivos de auto-pareamento/auto-indentação durante digitação. Projetada para ferramentas de agente que podem executar JavaScript no mundo principal da página (como Playwright, Puppeteer, ou scripts de usuário), ela é essencial para qualquer automação de páginas web que envolvam editores de código embutidos.

Funciona com~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/omrehbein/agent-skills/tree/main/skills/ace-editor

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Automação de editor Ace

O Ace renderiza apenas as linhas na tela e recebe pressionamentos de tecla através de uma textarea oculta que fecha automaticamente aspas/chaves e auto-indenta novas linhas. Digitar {, Enter, "a": [1], Enter, } em um editor JSON Ace padrão produz:

{
        "a": [1]
}
}

Portanto:

  • Leia e escreva através da API do editor com JavaScript executado na página. Não extraia elementos .ace_line ou capturas de tela para obter o conteúdo, e não digite código tecla por tecla.
  • Execute no mundo principal da página. Propriedades que a página define em elementos (como el.env) são invisíveis de mundos isolados, ex. scripts de conteúdo de extensão de navegador. Use a opção de mundo principal da sua ferramenta, ou o fallback no passo 5.
  • Respeite editores somente leitura. A API ainda pode alterá-los, mas o aplicativo não espera por isso — faça apenas quando o usuário pedir.
  • Alterar o editor não é salvar. Confirme com o usuário antes de clicar em Salvar / Enviar / Aplicar.

Os trechos abaixo são JavaScript puro da página cuja última expressão é o resultado. O passo 7 usa await; se sua ferramenta não tiver await de nível superior, envolva o trecho em uma função assíncrona (ex. page.evaluate(async () => { ... }) do Playwright).

1. Encontrar os editores

O renderizador do Ace adiciona a classe ace_editor a todo contêiner de editor, em todas as versões, então o seletor é estável. O que varia é onde os editores estão (documento principal, shadow roots abertos, iframes de mesma origem), quais elementos são editores reais (o popup de autocomplete também é um editor Ace) e se a instância é acessível. Pesquise dinamicamente:

(() => {
  const hits = [];
  const walk = (root, where) => {
    for (const el of root.querySelectorAll('.ace_editor')) hits.push({ el, where });
    for (const node of root.querySelectorAll('*')) {
      if (node.shadowRoot) walk(node.shadowRoot, `${where} > ${node.localName}::shadow`);
    }
    for (const frame of root.querySelectorAll('iframe, frame')) {
      let doc = null;
      try { doc = frame.contentDocument; } catch {}
      if (doc) walk(doc, `${where} > iframe${frame.id ? '#' + frame.id : ''}`);
      else hits.push({ el: null, where: `${where} > cross-origin iframe ${frame.src}` });
    }
  };
  walk(document, 'document');
  // o popup de autocomplete também é um editor Ace: pule-o
  const found = hits.filter(h => !h.el?.classList.contains('ace_autocomplete'));
  window.__aceEditors = found.map(h => h.el);
  return found.map(({ el, where }, i) => {
    if (!el) return { i, where, note: 'não acessível desta página: abra a URL do frame diretamente' };
    const ed = el.env?.editor;
    return {
      i,
      where,
      id: el.id || undefined,
      label: el.closest('[aria-label]')?.getAttribute('aria-label') || undefined,
      visible: el.getClientRects().length > 0,
      handle: !!ed,
      mode: ed?.session.getMode().$id,
      readOnly: ed?.getReadOnly(),
      lines: ed?.session.getLength(),
      firstLine: (ed ? ed.session.getLine(0) : el.querySelector('.ace_line')?.textContent ?? '').slice(0, 60),
    };
  });
})()
  • Escolha o alvo por label, id, mode, firstLine, visible; use seu i nos próximos passos (os exemplos usam 0).
  • Execute novamente o localizador após navegação ou re-renderizações. Uma lista vazia em um aplicativo de página única geralmente significa que o editor ainda não foi montado: aguarde e tente novamente.
  • handle: false → a página construiu o editor sem ace.edit() (sem el.env) ou você está em um mundo isolado: use o passo 5. Nunca chame ace.edit(el) para obter um handle — em um elemento sem env, ele cria um segundo editor sobre o editor da página.

2. Inspecionar e ler

const editor = window.__aceEditors[0].env.editor; // índice do localizador
({
  mode: editor.session.getMode().$id, // ex. ace/mode/json (ace/mode/text enquanto um modo ainda está carregando)
  readOnly: editor.getReadOnly(),
  lines: editor.session.getLength(),
  indent: JSON.stringify(editor.session.getTabString()),
  cursor: editor.getCursorPosition(), // { row, column }, base 0
  selection: editor.getSelectedText(),
  text: editor.getValue(),
})

Para documentos muito grandes, leia fatias com editor.session.getLines(firstRow, lastRow) (base 0, inclusivo).

3. Substituir o documento inteiro

const editor = window.__aceEditors[0].env.editor;
const newText = '...'; // o novo conteúdo completo
editor.session.getUndoManager().startNewGroup?.(); // mantenha esta alteração como seu próprio passo de desfazer
editor.setValue(newText, -1); // -1 coloca o cursor no início; dispara 'change' para o aplicativo atualizar seu estado
editor.getValue() === newText

Gere conteúdo estruturado a partir de dados em vez de escrevê-lo manualmente: JSON.stringify(data, null, editor.session.getTabString()) corresponde à indentação do editor.

4. Edições direcionadas

const editor = window.__aceEditors[0].env.editor;
// primeira correspondência; opções: wrap, caseSensitive, wholeWord, regExp, backwards, range
if (editor.find('oldValue', { wrap: true, caseSensitive: true })) editor.replace('newValue');
// toda correspondência, retorna a contagem; com regExp, retroreferências $1 funcionam
const replaced = editor.replaceAll('version-$1', { needle: 'v(\\d+)', regExp: true });
// por posição: linhas e colunas são base 0, colunas além do final da linha são cortadas
editor.session.replace({ start: { row: 2, column: 0 }, end: { row: 2, column: Infinity } }, 'linha 3 inteira nova');
 editor.session.insert({ row: editor.session.getLength(), column: 0 }, '\\nlinha anexada');
({ replaced, text: editor.getValue() })

Mais — cursor, seleção, dobramento, rolagem, comandos, eventos: referencias/ace-api.md.

5. Sem handle? Cole na textarea oculta

const el = window.__aceEditors[0]; // funciona com handle: false
const newText = '...';
const input = el.querySelector('textarea.ace_text-input');
const mac = /Mac|iPhone|iPad/.test(navigator.platform);
input.focus();
// selecionar tudo: Ace vincula Ctrl+A, ou Cmd+A em plataformas Apple
input.dispatchEvent(new KeyboardEvent('keydown', { key: 'a', code: 'KeyA', keyCode: 65, which: 65, ctrlKey: !mac, metaKey: mac, bubbles: true, cancelable: true }));
const data = new DataTransfer();
data.setData('text/plain', newText);
input.dispatchEvent(new ClipboardEvent('paste', { clipboardData: data, bubbles: true, cancelable: true }));
'colado'
  • O texto colado é inserido literalmente: sem auto-pareamento, sem auto-indentação. Editores somente leitura o ignoram.
  • Alternativa com a textarea focada e tudo selecionado: document.execCommand('insertText', false, newText).
  • Sem JavaScript: coloque o texto na área de transferência do SO, clique dentro do editor, pressione Ctrl+A (Cmd+A no macOS), depois Ctrl+V (Cmd+V).
  • Sem um handle, você só pode ler linhas renderizadas (.ace_text-layer .ace_line, após o próximo quadro). Para o texto completo, selecione tudo e copie via área de transferência do SO.

6. Se você realmente precisar digitar

const editor = window.__aceEditors[0].env.editor;
const assists = ['behavioursEnabled', 'wrapBehavioursEnabled', 'enableAutoIndent', 'enableLiveAutocompletion', 'enableBasicAutocompletion', 'enableSnippets'];
window.__aceSavedOptions = Object.fromEntries(assists.filter(name => editor.$options[name]).map(name => [name, editor.getOption(name)]));
editor.setOptions(Object.fromEntries(Object.keys(window.__aceSavedOptions).map(name => [name, false])));
editor.focus();
window.__aceSavedOptions // restaurar após digitar: editor.setOptions(window.__aceSavedOptions)

Digite, depois restaure as opções. Prefira conteúdo de linha única e pressione Escape antes de começar. Blocos de código multilinha: grave a saída esperada do editor, não a entrada com auto-indentação.

7. Erros de sintaxe

const editor = window.__aceEditors[0].env.editor;
editor.getSession().setAnnotations([
  { row: 0, column: 0, text: 'Exemplo de aviso', type: 'warning' },
]);
(await editor.session.getAnnotations())

Habilidades Relacionadas