Pular para o conteúdo

Pointer-interaction TDDs for desktop UI (hit-test routing · pointer capture · wheel scroll-step · gesture cancellation · title-bar press)

develop specs/develop/pointer-interaction-tdds.kmd

The Koder Stack test generators MUST emit pointer-interaction tests for every POINTER-DRIVEN DESKTOP UI surface of every component — the class of surface that gesture-tdds.kmd explicitly puts out of scope ("pointer-only desktop surfaces"). These are mouse/trackpad invariants that don't depend on the toolkit: a fresh press hit-tests to the region UNDER the pointer (never a region holding capture); a discrete wheel notch scrolls a fixed number of lines, invariant to font/cell size; a new gesture cancels an in-flight one and releases capture; a title-bar press double-clicks to maximize/restore and single-drags to rearrange. Created as the regression-tests.kmd §7 generator-coverage backfill for a cluster of kterm (Kroma) interaction bugs that had no generator to catch their class. FIRST target is Kroma — the canonical Koder pointer-desktop UI engine — whose generation target (the "Kora generates TDDs for its own Rust components" capability) is kroma#092: it reuses the #088 introspection export as the assert oracle and synthetic Event::Pointer* into the tile/widget tree as the input driver.

Quando esta spec se aplica

Triggers primários

Todos os triggers

Corpo da especificação

Spec — Pointer-interaction TDDs for desktop UI

Scope

Every pointer-driven desktop Koder UI surface MUST ship pointer-interaction TDDs under <module>/tests/regression/pointer/. This is the complement of gesture-tdds.kmd (touch) — it owns the mouse/trackpad invariants that the gesture spec lists "out of scope (pointer-only desktop surfaces)".

Primary targets:

  • Kroma apps (kroma-kterm, kolide, kompose, every Kroma-rendered surface) — the Kore-native render engine. Generation target = kroma#092 (synthetic Event::Pointer* into the tile/widget tree + #088 introspection as the assert oracle, headless).
  • Flutter-desktop / web pointer surfaces — via the existing pointer driver (koder-vdev pointer, synthetic-input-and-virtual-devices.kmd modality #2), absolute coordinates (R3 determinism).

Out of scope: touch (gesture-tdds.kmd), CLI/TUI, server-only services. A surface that is both touch and pointer ships both category sets.

R1 — Generation contract

For every component whose manifest declares a pointer-driven desktop surface with 2+ hit-test regions (panes/tiles/splits/overlays) OR a scrollable viewport OR a draggable title bar, /k-test MUST emit categories P1–P5 below into <module>/tests/regression/pointer/. Driven per surface (R1 of synthetic-input): Kroma → the Kore-native harness (kroma#092); Flutter/web → koder-vdev pointer. All input uses absolute coordinates (synthetic-input R3) so hit-test fidelity is exact + repeatable.

Categories

P1 — Fresh-press hit-targets the region under the pointer (capture independence)

When a new pointer press occurs at (x,y) while another region holds pointer capture (an in-flight drag/selection), the press routes to the region under (x,y)never the captor, and never the first region in tree/z order. Test: hold capture in region A (begin a drag), press in region B → assert B receives the press and A does not; any context action (menu) anchors in B at (x,y). Capture (is_forced) keeps only the ONGOING drag's moves/up reaching its region. Origin: kterm right-click opened the context menu in the wrong pane (fix a9c5063cc0).

P2 — Wheel scroll-step is N lines/notch, font-zoom invariant

A discrete wheel notch scrolls a fixed number of lines (Stack default 3, Tilix/VTE parity), scaled by the surface's real cell/line height so the line count is invariant to font zoom; a trackpad pixel-delta stays pixel-precise (not stepped). Test: one notch at cell height H → N lines; the same notch at 2H → still N lines; pixel-delta P → P px. Origin: kterm scrolled 1 line/notch, sluggish vs Tilix (fix dfbbe50f62).

P3 — A new gesture cancels the in-progress one

A press of a different button (or the start of a new gesture) ends any in-flight drag/selection and releases capture before starting its own action — so a stale gesture never keeps tracking the pointer. Test: begin a left-drag selection, then right-press → assert the selection ended (not "selecting") AND the menu opened; subsequent moves do not extend the (now-ended) selection. Origin: kterm right-press left the selection tracking the mouse (fix a9c5063cc0).

P4 — Capture handoff / no stuck capture

Pointer capture is released on the terminating event (up/cancel); a press that lands outside the captor does not leave the captor stuck holding capture (which would broadcast every later move to it). Test: drive a drag, drop outside the captor's bounds → assert capture cleared; a following move reaches only the region under it.

P5 — Title-bar / header press semantics

On a surface with a draggable region header (tile/pane title bar): a double-click on the header toggles maximize/restore of that region; a single-press drag grabs the header to rearrange the region to a drop edge; a press on the header does NOT start text selection in the content. Test: double-click header → region maximized, double-click again → restored; press-drag header from region A onto region B's edge → A moves to that edge. Origin: kterm's 24-jun binary predated header drag-rearrange + double-click-maximize (present + tested in origin; this category guards the class against future regression).

R2 — Verdict + gate

Each category emits deterministic, headless tests (logic/state, no human eye). A failing category is a release-gate fail (/k-test --release / /k-ship), same as gesture/visual. Per regression-tests.kmd §7, a component-local regression (the floor) plus generated P1–P5 (the ceiling) are both required where applicable.

Ratification

RATIFIED 2026-07-07 (owner /k-go). Created per regression-tests.kmd §7 (Generator-coverage backfill, owner directive 2026-06-07) from the kterm pointer-interaction bug cluster (2026-06-30). The generation target that makes these reach Kroma (Kore-native Rust) is the epic kroma#092 — now complete and declaration-driven: /k-test-gen-pointer reads the pointer-desktop surface declared in a module's koder.manifest.json (interaction-visual-parity.kmd §S1) and emits P1–P5 as Rust tests via the kroma_test_state harness + the #088 introspection oracle. As a mandatory: true spec it is now wired as a spec-triggers.kmd gate (action: ship/change a pointer-driven desktop UI surface, or author/regenerate pointer-interaction TDDs) — the CLAUDE.md trigger table lists it.

Referências