CommunityCodierung & Entwicklunggithub.com

omrehbein/ace-editor

Verwenden, wenn ein Agent, der eine Webseite automatisiert, Text in einem Ace-Code-Editor (Elemente mit der Klasse `ace_editor`) finden, lesen, ersetzen oder validieren muss – Code/JSON/YAML/SQL-Editoren, die in Cloud-Konsolen (z. B. AWS-Richtlinien-Editoren), Cloudscape/awsui, Cloud9-ähnlichen IDEs, react-ace oder Admin-Panels eingebettet sind. Auch verwenden, wenn Text, der in einen Web-Code-Editor eingegeben wurde, verstümmelt ankommt (zusätzliche oder fehlende Anführungszeichen/Klammern, doppelte Einrückung), wenn nur ein Teil eines Dokuments im DOM oder auf einem Screenshot sichtbar ist, oder wenn Sie die Syntaxfehler des Editors benötigen.

Was ist ace-editor?

Dieses Skill ermöglicht es einem KI-Agenten, Ace-Code-Editoren in Webseiten zuverlässig zu automatisieren, die in vielen Cloud-Konsolen (wie AWS), IDEs (Cloud9) und Admin-Panels verwendet werden. Da Ace nur die sichtbaren Zeilen rendert und Tastatureingaben über ein verstecktes `textarea` empfängt, das automatisch Anführungszeichen/Klammern schließt und automatisch einrückt, führt das direkte Eintippen von Code oft zu fehlerhaften Ergebnissen wie doppelten Einrückungen oder fehlenden/überschüssigen Zeichen. Das Skill umgeht diese Probleme, indem es die Ace-JavaScript-API im Hauptkontext der Seite verwendet, um Inhalte zu lesen, zu ersetzen und zu bearbeiten, ohne das fehleranfällige DOM-Scraping oder Tastatur-Simulation. Es unterstützt das Auffinden aller Ace-Editoren auf einer Seite (auch in Shadow-Roots und iframes), das Lesen des vollständigen Editorinhalts (auch bei großen Dokumenten über Zeilenbereiche), das vollständige Ersetzen des Dokuments, gezielte Suchen und Ersetzungen (mit regulären Ausdrücken), sowie eine Fallback-Lösung zum Einfügen von Text in das versteckte Textarea, falls die Editor-API nicht zugänglich ist. Das Skill wird für Agenten auf allen Plattformen empfohlen, die benutzerdefinierte JavaScript-Snippets in der Seite ausführen können (wie Cursor, Codex, Playwright), und enthält detaillierte Schritt-für-Schritt-Anleitungen sowie Code-Snippets, die direkt kopiert und verwendet werden können.

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

Installed? Explore more Codierung & Entwicklung skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Ace-Editor-Automatisierung

Ace rendert nur die auf dem Bildschirm sichtbaren Zeilen und empfängt Tastatureingaben über ein verstecktes textarea, das automatisch Anführungszeichen/Klammern schließt und neue Zeilen automatisch einrückt. Die Eingabe von {, Enter, "a": [1], Enter, } in einen standardmäßigen Ace-JSON-Editor ergibt:

{
        "a": [1]
}
}

Daher:

  • Lesen und Schreiben über die Editor-API mit JavaScript, das auf der Seite ausgeführt wird. Kratzen Sie keine .ace_line-Elemente oder Screenshots nach Inhalten ab, und tippen Sie keinen Code Tastendruck für Tastendruck ein.
  • Im Hauptkontext der Seite ausführen. Eigenschaften, die die Seite auf Elemente setzt (z. B. el.env), sind aus isolierten Kontexten unsichtbar, z. B. Browsererweiterungs-Content-Scripts. Verwenden Sie die Hauptkontext-Option Ihres Tools oder den Fallback aus Schritt 5.
  • Schreibgeschützte Editoren respektieren. Die API kann sie zwar trotzdem ändern, aber die App erwartet das nicht – tun Sie es nur, wenn der Benutzer es verlangt.
  • Die Änderung des Editors speichert nicht. Bestätigen Sie mit dem Benutzer, bevor Sie auf Speichern / Senden / Anwenden klicken.

Die folgenden Snippets sind einfaches JavaScript der Seite, dessen letzter Ausdruck das Ergebnis ist. Schritt 7 verwendet await; wenn Ihr Tool kein await auf oberster Ebene hat, verpacken Sie das Snippet in eine asynchrone Funktion (z. B. Playwright page.evaluate(async () => { ... })).

1. Die Editoren finden

Aces Renderer fügt jedem Editor-Container in jeder Version die Klasse ace_editor hinzu, daher ist der Selektor stabil. Was variiert, ist wo Editoren leben (Hauptdokument, offene Shadow-Roots, iframes gleicher Herkunft), welche Elemente echte Editoren sind (das Autovervollständigungs-Popup ist auch ein Ace-Editor) und ob die Instanz erreichbar ist. Dynamisch suchen:

(() => {
  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');
  // das Autovervollständigungs-Popup ist auch ein Ace-Editor: überspringen
  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: 'von dieser Seite nicht erreichbar: öffnen Sie die Frame-URL direkt' };
    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),
    };
  });
})()
  • Wählen Sie das Ziel nach label, id, mode, firstLine, visible aus; verwenden Sie dessen i in den nächsten Schritten (die Beispiele verwenden 0).
  • Führen Sie den Finder nach Navigation oder Neu-Renderings erneut aus. Eine leere Liste in einer Single-Page-App bedeutet normalerweise, dass der Editor noch nicht eingehängt ist: warten und wiederholen.
  • handle: false → die Seite hat den Editor ohne ace.edit() erstellt (kein el.env) oder Sie befinden sich in einem isolierten Kontext: verwenden Sie Schritt 5. Rufen Sie niemals ace.edit(el) auf, um einen Handle zu erhalten – bei einem Element ohne env erstellt dies einen zweiten Editor über dem der Seite.

2. Inspizieren und lesen

const editor = window.__aceEditors[0].env.editor; // Index aus dem Finder
({
  mode: editor.session.getMode().$id, // z. B. ace/mode/json (ace/mode/text, während ein Modus noch lädt)
  readOnly: editor.getReadOnly(),
  lines: editor.session.getLength(),
  indent: JSON.stringify(editor.session.getTabString()),
  cursor: editor.getCursorPosition(), // { row, column }, 0-basiert
  selection: editor.getSelectedText(),
  text: editor.getValue(),
})

Für sehr große Dokumente lesen Sie Ausschnitte mit editor.session.getLines(firstRow, lastRow) (0-basiert, inklusive).

3. Das gesamte Dokument ersetzen

const editor = window.__aceEditors[0].env.editor;
const newText = '...'; // der vollständige neue Inhalt
editor.session.getUndoManager().startNewGroup?.(); // diese Änderung als eigenen Rückgängig-Schritt behalten
editor.setValue(newText, -1); // -1 setzt den Cursor an den Anfang; löst 'change' aus, sodass die App den Status aktualisiert
editor.getValue() === newText

Generieren Sie strukturierte Inhalte aus Daten, anstatt sie von Hand zu schreiben: JSON.stringify(data, null, editor.session.getTabString()) entspricht der Einrückung des Editors.

4. Gezielte Bearbeitungen

const editor = window.__aceEditors[0].env.editor;
// erste Übereinstimmung; Optionen: wrap, caseSensitive, wholeWord, regExp, backwards, range
if (editor.find('oldValue', { wrap: true, caseSensitive: true })) editor.replace('newValue');
// jede Übereinstimmung, gibt die Anzahl zurück; mit regExp funktionieren $1-Rückverweise
const replaced = editor.replaceAll('version-$1', { needle: 'v(\\d+)', regExp: true });
// nach Position: Zeilen und Spalten sind 0-basiert, Spalten jenseits des Zeilenendes werden abgeschnitten
editor.session.replace({ start: { row: 2, column: 0 }, end: { row: 2, column: Infinity } }, 'ganze neue Zeile 3');
editor.session.insert({ row: editor.session.getLength(), column: 0 }, '\\nangehängte Zeile');
({ replaced, text: editor.getValue() })

Mehr – Cursor, Auswahl, Faltung, Scrollen, Befehle, Ereignisse: references/ace-api.md.

5. Kein Handle? In das versteckte Textarea einfügen

const el = window.__aceEditors[0]; // funktioniert auch mit handle: false
const newText = '...';
const input = el.querySelector('textarea.ace_text-input');
const mac = /Mac|iPhone|iPad/.test(navigator.platform);
input.focus();
// alles auswählen: Ace bindet Strg+A oder Cmd+A auf Apple-Plattformen
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 }));
'eingefügt'
  • Eingefügter Text wird wörtlich eingefügt: keine automatische Paarung, keine automatische Einrückung. Schreibgeschützte Editoren ignorieren es.
  • Alternative, wenn das Textarea fokussiert und alles ausgewählt ist: document.execCommand('insertText', false, newText).
  • Ohne JavaScript: Den Text in die Zwischenablage des Betriebssystems legen, in den Editor klicken, Strg+A (Cmd+A auf macOS) drücken, dann Strg+V (Cmd+V).
  • Ohne Handle können Sie nur gerenderte Zeilen lesen (.ace_text-layer .ace_line, nach dem nächsten Frame). Für den vollständigen Text alles auswählen und über die Betriebssystem-Zwischenablage kopieren.

6. Wenn Sie wirklich tippen müssen

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 // nach dem Tippen wiederherstellen: editor.setOptions(window.__aceSavedOptions)

Tippen, dann die Optionen wiederherstellen. Bevorzugen Sie einzeilige Inhalte und

Verwandte Skills