Loading...
Loading...
Embed a headless iOS Simulator in Codex++ right panel with mirroring, touch input, and UI element annotations
npx skill4agent add aradotso/codex-skills codex-plusplus-ios-simulator-tweakSkill by ara.so — Codex Skills collection.
Simulator.appsudo xcode-select -s /Applications/Xcode.appxcode-select -p
# Should output: /Applications/Xcode.app/Contents/Developermkdir -p ~/Library/Application\ Support/codex-plusplus/tweaks/
cd ~/Library/Application\ Support/codex-plusplus/tweaks/
git clone https://github.com/b-nnett/codex-plusplus-ios-simulator.git ios-simulator~/Library/Application Support/codex-plusplus/tweaks/ios-simulator/+Cmd+Y{
"element": {
"label": "Sign In",
"role": "Button",
"frame": { "x": 120, "y": 450, "width": 175, "height": 44 }
},
"simulator": "iPhone 15 Pro",
"marker": { "x": 207.5, "y": 472 },
"viewport": { "width": 393, "height": 852 }
}ios-simulator/
├── index.js # Main Codex++ tweak entry point
├── helpers/
│ ├── sim-capture.swift # Headless frame capture helper
│ └── sim-input.m # Touch, keyboard, hardware button helper
├── manifest.json # Tweak metadata
└── README.mdmodule.exports = {
name: 'iOS Simulator',
icon: 'phone.fill',
// Called when panel is opened
onActivate(panel) {
panel.setTitle('iOS Simulator');
initializeSimulator(panel);
},
// Called when panel is closed
onDeactivate() {
stopHelpers();
cleanupResources();
},
// Panel UI setup
renderPanel(container) {
const canvas = document.createElement('canvas');
const controls = createControlBar();
container.append(canvas, controls);
return { canvas, controls };
}
};import Foundation
import CoreSimulator
import IOSurface
// Connect to simulator framebuffer
let device = SimDevice(udid: deviceUDID)
let surface = device.surface // IOSurface
// Capture loop
while isRunning {
let surfaceRef = device.io.surface
let baseAddress = IOSurfaceGetBaseAddress(surfaceRef)
let width = IOSurfaceGetWidth(surfaceRef)
let height = IOSurfaceGetHeight(surfaceRef)
// Write raw frame to stdout
fwrite(baseAddress, 1, width * height * 4, stdout)
fflush(stdout)
usleep(16667) // ~60fps
}#import <Foundation/Foundation.h>
#import <SimulatorKit/SimulatorKit.h>
// Send touch event
- (void)sendTouchAtX:(CGFloat)x y:(CGFloat)y phase:(NSString*)phase {
SimDevice *device = [self deviceWithUDID:deviceUDID];
SimDeviceIOClient *io = device.io;
SimDeviceIOTouchEvent *event = [SimDeviceIOTouchEvent new];
event.x = x;
event.y = y;
event.phase = [self phaseFromString:phase]; // began, moved, ended
[io sendTouchEvent:event];
}
// Send keyboard input
- (void)sendKeyPress:(NSString*)key {
SimDevice *device = [self deviceWithUDID:deviceUDID];
[device.io sendKeyboardEvent:key];
}
// Hardware buttons
- (void)pressHomeButton {
SimDevice *device = [self deviceWithUDID:deviceUDID];
[device.io pressButton:SimDeviceIOButtonHome];
}const { execSync } = require('child_process');
function getAvailableSimulators() {
const output = execSync('xcrun simctl list devices --json', { encoding: 'utf8' });
const data = JSON.parse(output);
const devices = [];
for (const runtime in data.devices) {
for (const device of data.devices[runtime]) {
if (device.isAvailable) {
devices.push({
udid: device.udid,
name: device.name,
state: device.state,
runtime: runtime
});
}
}
}
return devices;
}function bootSimulator(udid) {
execSync(`xcrun simctl boot ${udid}`, { encoding: 'utf8' });
}function shutdownSimulator(udid) {
execSync(`xcrun simctl shutdown ${udid}`, { encoding: 'utf8' });
}~/Library/Caches/co.bennett.ios-simulator/
├── sim-capture # Compiled Swift helper
└── sim-input # Compiled Objective-C helper{
"name": "iOS Simulator",
"version": "1.0.0",
"description": "Headless iOS Simulator for Codex++",
"entry": "index.js",
"permissions": [
"process",
"filesystem"
],
"shortcuts": [
{
"key": "cmd+y",
"action": "toggleSimulator"
}
]
}# Ensure Xcode is installed
xcode-select -p
# If pointing to Command Line Tools, fix with:
sudo xcode-select -s /Applications/Xcode.app# List available simulators
xcrun simctl list devices
# Download iOS runtimes in Xcode:
# Xcode → Settings → Platforms → [Download iOS runtime]# Verify swiftc is available
which swiftc
# Should be under Xcode.app/Contents/Developer/
# If not, run: sudo xcode-select -s /Applications/Xcode.app# SimulatorKit is a private framework included with Xcode
# Ensure full Xcode is installed, not just Command Line Tools
# Check framework exists:
ls /Applications/Xcode.app/Contents/Developer/Library/PrivateFrameworks/SimulatorKit.frameworkxcrun simctl shutdown <UDID>xcrun simctl spawn booted log stream --predicate 'subsystem == "com.apple.CoreSimulator"'xcrun simctl list devices | grep Bootedps aux | grep sim-captureCmd+YSimulator.appsim-inputps aux | grep sim-input// SwiftUI
Text("Sign In")
.accessibilityLabel("Sign In Button")
.accessibilityIdentifier("signInButton")
// UIKit
button.accessibilityLabel = "Sign In Button"
button.accessibilityIdentifier = "signInButton"Cmd+Yxcrun simctl launch <UDID> com.yourcompany.yourapp// Custom extension to record touch sequences
const touches = [];
panel.on('touch', (x, y, phase) => {
touches.push({ x, y, phase, timestamp: Date.now() });
});
panel.on('annotate', () => {
// Include touch sequence in annotation
return { touches, viewport, element };
});