e2e-rpc
E2E-Steuerung von zid über den RPC-Port 9999 und den Interactive-Modus: RPC-Referenz (key_press, ui_state, editor_state, element_bounds, get_active_tab, explorer_open), Screenshot-Koordinaten, Threadi
SKILL.md.claude/skills/e2e-rpc/SKILL.md
GitHubE2E über RPC
Headless-Betrieb
--headless --ai=offführt denselben Frame-Loop aus wie das Fenster (Tab-Wechsel, Explorer-Klicks, Tab-Schließen, gepufferte Eingaben, Screenshots), nur ohne Fenster-Events, Cursor und Präsentation. Tests brauchen deshalb nie--e2emit Fenster; das stört den User.- Die UI-Uhr (
ui_time_ms, Tooltips nach 700 ms, Toasts, Hover) läuft mit der echten Zeit zwischen zwei Frames (Deckel 250 ms). Ein Headless-Frame mit Layout und RPC dauert ~35 ms; Tests, die auf Zeit warten, rechnen deshalb in Echtzeit, nicht in Frames. - Screenshot:
./tmp/vulkan-screenshot.ppm, per RPCecho '{"jsonrpc":"2.0","method":"screenshot","id":1}' | nc --send-only localhost 9999. - Headless-Screenshot ist 1200x800, Tab-Kopf liegt bei y≈105, Inhalt ab y≈130.
- Der SVG-Atlas rasterisiert max. 4 neue Icons pro Render-Durchgang; neue Icons erscheinen erst im zweiten Screenshot (Skripte rendern zweimal).
Interactive-Modus (stdin/stdout)
zig build run -- --interactive, zeilenbasiert; Handler wirken direkt, es gibt keinen Loop.
open <path> # Datei in neuem Tab
close-tab <n> # Tab nach Index schließen
switch-tab <n> # zu Tab wechseln
click <x> <y> # Mausklick
key <name> [ctrl] # Taste (enter, backspace, k, …)
type <text> # Text tippen
screenshot # -> ./tmp/vulkan-screenshot.ppm
split <h|v> # teilen
get-state # Zustand als JSON
shutdown # beenden
echo -e "open ./README.md\nget-state\nshutdown" | zig build run -- --interactive
Threading der RPC-Handler
- Handler laufen im Server-Thread.
click,right_click,move_mouse,key_press,type_textundscreenshotwerden gepuffert und vom Main-Thread pro Frame angewendet (drainInputs/serviceScreenshot);close_active_tabgeht überpending_tab_closes. - Ein Handler braucht einen Rückgabewert (etwa
"ok"): mit!voidschickt zigjr keine Antwort, und der Aufrufer wartet bis zum Zeitlimit. open_fileprüft nur IsDir synchron und legt den Tab gepuffert an, weil eintabs.appendaus dem Server-ThreadrenderTabBarmitten in der Iteration trifft. Nachopen_filealsosettle, bevor Tabs abgefragt werden.open_folderläuft ebenso gepuffert im Main-Thread, weilloadDirectorysonst Knoten unter einem laufenden Render leert.split_paneundshow_context_menumutieren noch direkt aus dem Server-Thread.onMainist Pflicht für jeden RPC, der veränderlichen UI-Zustand liest (Buffer, Editoren, Listen der UI): der Server-Thread legt den Aufruf ab,drainInputsführt ihn im nächsten Frame aus, der Server wartet (5 s Zeitlimit). Beispiele:editor_state,file_text,get_chat_input,chatState,explorer_entries. OhneonMainliest der Handler Buffer oder Bäume, die der Main-Thread gerade ersetzt: sporadisch fehlende Einträge, Zeiger in freigegebenen Speicher, „switch on corrupt value“ inBuffer.walk_const. Stresstest:python3 scripts/e2e_rpc_race.py.- Übrige Lesezugriffe auf gemeinsame Daten brauchen eine Sperre (Beispiel
git_status_mutex, Skillexplorer).
RPC-Referenz
agent_tool(name, arguments_json)führt ein Agent-Werkzeug (read_file,write_file,replace_text, …) direkt aus, bestätigt und ohne Modell; Pfade relativ zum Projektordner. Ergebnis ist der Text, der ans Modell ginge (e2e_encoding.py).explorer_open <path>simuliert einen Klick im File-Explorer (setztfile_to_open),open_filegeht nur über die Tab-Leiste.get_active_tabliefert pro Tabmodifiedsowieeditor_modifiedundeditor_file(Buffer, den der Editor gerade zeigt).key_press(name, ctrl)kennt alle Buchstaben a–z sowie enter, backspace, escape, delete, tab, grave, up/down/left/right, home/end, page_up/page_down, f1, f2;key_press_mods(name, ctrl, shift)zusätzlich Shift (Ctrl+Shift+Tab). Modifier werden vor und nach der Taste gesetzt bzw. gelöscht, ein hängender Modifier lässt sich mit ihnen also nicht nachstellen.key_press_holdhält sie bismods_release;window_unfocusspielt den Fokusverlust des Fensters nach (Alt+Tab).key_press_altundclick_mods: Skilleditor.ui_stateliefert Dialog-Titel, offenes Menü, gehaltene Modifier (mods_down), Explorer-Fokus, Explorer sichtbar, Picker/Shortcut-Dialog offen, Tabs (Pfad, Art, geändert) und aktiven Tab,tooltip(Text im letzten Frame),last_frame_ms/max_frame_ms(Layout-Zeit; headless rendert nur beim Screenshot), die Glyph-Cache-Diagnoseglyph_rasterized,glyph_cache_clears,glyph_cache_entries, dazutext_runs_dropped(nicht gezeichnete Textstücke) undclay_errors(Clay-Fehler seit Start). Damit prüfen E2E, dass nichts still verloren ging (scripts/e2e_long_runs.py).editor_stateliefert Zeilen, Cursor, Suchleiste (offen, Begriff, kein Treffer), den Text sowieheight/visible_rows(Bounding-Box des Editors aus dem Vorframe, muss über Frames konstant bleiben).element_bounds(id)/element_bounds_i(id, index)geben Clay-Bounding-Boxen für Klicks. Für „existiert das Element gerade?“ sind sie unzuverlässig (Clay behält Daten verschwundener Elemente), dafürui_state.- Explorer-RPCs (
explorer_entries,scroll): Skillexplorer. Ordner-Dialog (folder_picker_state,get_state.root): Skillprojektordner.
Fixtures
Fixtures unter tmp/ anlegen (gitignored, im Explorer sichtbar). Keine Suite liest aus
test_data/, und zid öffnet beim Start nur eine Datei von der Kommandozeile. Eingecheckte
Vorlagen liegen unter scripts/fixtures/; PDF und PNG erzeugt scripts/e2e_fixtures.py ohne
Fremdbibliothek (write_pdf, write_png; Selbsttest per Direktaufruf). Start, Stopp und
Suitenbetrieb (start_zid, nie parallel): Skill grosse-dateien.