Hover rules in Rust, step A: the hover card machine, tooltip intent, safe triangle, row card placement and group summary, with their C ABI (no Swift change)

Merged
LA
3d ago
+4212-4

Step A of the hover FFI move (B8): the hover rules in Rust, with their C ABI and JSON op. No Swift change and no behavior change: the app keeps its Swift rules until step B (#31836) switches it to these, after the bot's CCmuxAppFFI re-pin is on feat.

What lands:

  • cmux-motion hover: the hover card state machine (phases, delays, aiming, bridging, pins, an owner's card), the tooltip hover intent, the safe triangle (CGRect.isAimedAt), the sidebar row card's placement and zones (HoverCardEscapeGeometry, room, row band) and the hover timing tokens. Leo's and Lawrence's values are copied unchanged, each with its history, into hover/timing.rs.
  • cmux-layout-reducer group_hover_summary: the group card summary (GroupHoverCardSummary.make) as typed lines; the app keeps the localized words. It keeps Swift's String rules: values are equal after NFC, and port lists split on grapheme clusters that are exactly ",". unicode-normalization and unicode-segmentation are already in the app FFI lock (cmux-palette-ranker, cmux-focus-core), so no new package reaches the app.
  • The C ABI: cmux-app-ffi/src/hover.rs and the Hover section of cmux_motion_ffi.h (ABI version 4). The machine is a plain C struct the app keeps; one call applies a batch of typed events and writes the effects into a caller buffer (no JSON, no allocation per pointer move). The struct forms and their total conversions are cmux_motion::hover::c, so the workspace tests cover them, layouts included. The group summary is the JSON op group_hover_summary on cmux_layout_reducer_json (once per card show).

The Swift tests of these rules are ported as Rust tests: WorkspaceHoverCardBehaviorTests, HoverCardEscapeGeometryTests, HoverCardRowAlignmentTests, HoverCardWindowClampTests, GroupHoverCardSummaryTests and the machine and intent halves of TabHoverCardTimingTests and TooltipHoverIntentTests, plus the branches only AppKit tests reached (pins, an owner's card, removed targets, lingering cards), a 20 000-event walk that bounds effects per event, and the C forms.

Check the app FFI pin: expected, step A of a two-step FFI move.

Tests

scripts/ci/cmux-tui-rust-check.sh on the fleet (Linux):

Risk

risk:normal. Nothing calls the new symbols yet; the app links them only after the re-pin.

Changelog

none

Observed / Not run

Observed: the fleet red and green runs and clippy above; the pre-push gate (no new violations).

Not run:

  • Fmt: base finding. cmux-tui-rust-check.sh fmt (jobs eed2d3a4f24560587d3079ce, b6392995e2e2a3e64abcadbc) fails only on acpmux/src/hub/list_guard.rs:837, :925 and hub/mod.rs:1372, which this PR does not touch; the hover files are clean.
  • cmux-layout-reducer-ffi workspace tests (tests/group_hover_summary.rs): not in the fleet script; the cmux-tui CI job runs them.
  • Swift: nothing changes here. Step B (#31836) runs the Swift suites on a Mac and the perf A/B of a sidebar hover sweep.
  • No capture: no UI changes.