rightkit-control
Shared native desktop control APIs for Right Suite applications. Provides JSON helpers, keyboard input, WebDriver bridging, and platform-specific accessibility or embedded control modules.
Hidden macOS pointer input
Embedded Tauri targets receive native NSEvent down/drag/up calls on the
WKWebView responder, on AppKit's main thread. This bypasses window first-mouse
handling without making a window key, showing it, or activating its app.
WebKit reads +[NSEvent pressedMouseButtons] to build Pointer Events, so the
embedded path supplies a thread-local button mask during dispatch. A second
scoped hook bypasses WKWebView's asynchronous pointer text-input interception;
both Objective-C hooks forward to their original implementations outside that
dispatch. No DOM input events or global HID events are synthesized.
pointer {phase:"down"|"drag"|"up"|"move",x,y,button?,modifiers?} supports
scrubs split across requests. click, move, and composite drag share native
delivery; x/y are viewport CSS pixels. Pointer calls retain effect admission,
allowlists, and optional input-lease fencing. rightkit-qa::Control::pointer
uses this service for hidden launches. Non-embedded callers keep the per-pid
CGEvent fallback, which cannot guarantee first-mouse delivery to inactive views.
examples/control-test-app/proof.sh proves trusted Pointer Events, capture,
scrub value changes, modifier clicks, hidden/non-key readback, and foreground
stability throughout the run. TARGET names the workspace managed debug
directory; APP_TARGET names the standalone fixture's managed debug directory.
The tauri-plugin feature enables the embedded Tauri control server.
Native viewport sizing
set_viewport {width:1440,height:1000} accepts integer CSS dimensions in
1..=16384 & returns measured innerWidth/innerHeight. It passes command
effect admission & never activates, orders, or makes windows key. macOS computes
frameRectForContentRect: on AppKit's main thread & temporarily subclasses only
target NSWindow to bypass constrainFrameRect:toScreen: during setFrame:display:NO.
Original class is restored immediately. Windows uses SetWindowPos with no move,
z-order change, or activation. Other platforms return typed unsupported.
macOS explicitly resizes hidden WKWebView & completes native layout before polling page dimensions outside AppKit's main-thread block. Readback retries zero/stale dimensions & transient JS failures for up to 2 s, including callback timeouts; failure reports requested & last measured dimensions.
rightkit-qa::Control::set_viewport(width, height) returns measured dimensions.
WKWebView snapshots explicitly cover full bounds; fixture proof checks hidden
1440×1000 CSS content, scale-aware PNG dimensions & foreground stability.
Publish through the RightKit release workflow.
Control-plane guarantees (0.1.2)
- EFF-001 admission (
admission::EffectGate): every effectful request is validated, approved by a hostAdmissionHook(trait or closure), executed, and settled asok | denied | failed | cancelled. A denial (or a panicking hook) happens before any side effect.webdriver::Server::with_gate(click, send keys, screenshot), thetauri-pluginControl::gate,mac::Gatedand therightkit-controlCLI route every effectful entry point through it; the default gate is allow-all, so existing behaviour is unchanged. - PTY-002 input lease (
lease::InputLease,lease::ControlSession): acquire bumps the epoch and fences the previous holder; input carries(epoch, sequence); stale epochs are rejected, acknowledged duplicates are deduplicated without re-execution, gaps are rejected, and ambiguous (unacknowledged) input must be explicitly reconciled before new input.Control::input_leasemakes the plugin's input methods requireleaseEpoch+inputSequenceand addslease_*methods. - PTY-002 durable state (
lease::InputLease::open,lease_store): optionalLeaseStorebacking (in-memory stays the default) with an atomicFileLeaseStore(temp file, fsync, rename, directory fsync, checksum). Every change is saved before it takes effect: an epoch is granted only once durable, and an input sequence is reserved durably before its executor runs. Each lease has a durableExecutionIdentityand anExecutionState(Running | Exited | Unknown); input is accepted only whileRunning. On restart the epoch is fenced forward (never backwards), the lease is detached,Runningcomes backUnknown, and input that was unacknowledged at the crash comes back asUnknowninput (unknown_input,input_status) that must be resolved explicitly withacknowledge_input(epoch, seq),reconcileoracquire_reconciling. A corrupt or foreign record fails closed rather than resetting. - Lifecycle events (
events::ControlEvent): content-free start/stop/crash/denied and lease acquired/fenced/released/rejected events via anEventSinkcallback.
CLI admission
rightkit-control [--policy FILE] [--events] <command> ... admits every
effectful command (launch click hover drag scroll click-text press keywin keyraw type key shot, and serve's click / send keys / screenshot) through
one gate. Without a policy the gate is allow-all (output unchanged); with
--policy FILE or RIGHTKIT_CONTROL_POLICY it applies cli::Policy
(default allow|deny, then first-match allow|deny <kind|*> [method|*]
lines). An unreadable or invalid policy exits 2; a denial exits 3 and does
nothing. --events / RIGHTKIT_CONTROL_EVENTS=stderr prints content-free
lifecycle events to stderr. Read-only commands are not effects.
Known gaps
Closed in 0.1.2:
- PTY-002 durable lease state and execution identity/state across host restart or crash (previously in-memory only).
- Ungated paths: the effectful
macprimitives are now crate-private behindmac::Gated, reachable ungated only through the explicitly namedmac::unsafe_ungatedopt-in; the CLI admits through a gate with an optional policy; the WebDriver screenshot is now admitted as acaptureeffect.
Remaining:
- Breaking for direct
maccallers:mac::click,type_text,key,ax_press,click_node,launch_hidden,capture_window,ax_make_key,key_without_raise,hover,drag,scrollmoved tomac::Gated(ormac::unsafe_ungated). Reads (windows,main_window,frontmost_*,ax_dump,ax_find,ax_text,accessibility_trusted) are unchanged. webdriver::macos::AxBackendis an adapter whoseBackendmethods act directly, andServer::backendis a public field; only calls throughServer::handle/serveare admitted.FileLeaseStoreassumes one owning process per file (no cross-process lock). If the post-execute acknowledgement save fails, the durable record stays conservative (that input recovers asUnknown) andInputLease::store_degraded()reports it.acquire_reconcilingpanics if a durable store refuses the save; durable callers should usetry_acquire_reconciling.- The holder is identified by its epoch only (no holder label is stored).
- Recovery happens in
InputLease::open, before an event sink is attached, so no recovery event is emitted; inspectrecord()after opening. LeaseErrorgained variants (ExecutionNotRunning,UnsentSequence,NonMonotonicAck,InvalidExecutionTransition,Store); exhaustive external matches need a new arm.- The
tauri-pluginlease methods do not yet expose durable state oracknowledge_inputover the wire.