You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(overlay): a click on a pin edits its comment, in both tools (VRT-u209)
A comment written with the FRAME tool could be read and never edited. Pins were
made hit-test-inert in frame mode so a press starting on one could still draw a
frame (VRT-dp47) — but going inert was only ever meant to stop a pin swallowing a
DRAG, and it also removed the only route to the editor. Reported as "I need to
click on the comment again to edit the text… right now when I hover on one of the
numbers, nothing happens".
Diagnosed by measurement rather than by reading: the probe's phase 11c presses a
real pin in each tool and printed editingNoteID=nil in frame mode, while point
mode opened the editor. That leg had PINNED the empty result as the desired
outcome, so the contract was half-written down and the gap looked deliberate.
The press is now routed by GEOMETRY at the catcher's release —
PinAttentionRule.pressedNote via SelectionGesture — so the pin view stays inert
and the pin stays live. TRAVEL, NOT TOOL, IS THE GATE: a click on a pin edits it
in either tool, a drag from a pin still draws its frame. One sentence true of
both, so the VRT-dp47 report cannot come back; mutation-verified (dropping the
gate fails exactly the two drag-from-pin tests) and measured by a new probe leg
that drags off a pin and asserts the frame still resolves.
The press radius is the pin's OWN (10pt), deliberately not the 20pt attention
radius: attending is free and may reach outside the pin, a click is scarce
because the same press could have selected the element underneath. Wired to
attentionRadius every pin would be a 40pt hole in the canvas that looks 20pt.
Highlighting the captured selection while its card is open needed no new
plumbing — attendedNoteID is editingNoteID ?? hoveredNoteID, so the open card
already wins the tie — but it was only covered for the hover path. It is now
asserted end-to-end in the tool it was broken in, and in the probe for both.
230 unit tests, 0 failures; probe 11/11 phases PASS against a live AX tree; iOS
module type-checks clean against the iPhoneOS SDK in Swift 6.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: PARITY.md
+2-1Lines changed: 2 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -22,7 +22,8 @@ row; each asymmetry is closed by code or has a tracked mitigation.
22
22
| The first click on the overlay acts |`FirstMouseHostingView` (`acceptsFirstMouse -> true`) on both panels' content views | n/a | ASYMMETRIC BY THE PLATFORM, not by the code: "a mouse-down in a non-key window is discarded unless the view accepts first mouse" is an AppKit window-activation rule with no UIKit equivalent — iOS has no key window a press must first buy, and a touch on the pass-through `UIWindow` is delivered on the first tap. Nothing to mirror; the fix lives entirely in the macOS host |
23
23
| Live selection follows the content on scroll |`translateSelection(by:within:)`, driven by the same measurement `KeyablePanel.scrollWheel` already makes | none | SAME asymmetry and the same reason as the row for captured notes below: the correction is affordable on macOS only because the overlay panel intercepts every wheel event and drives the clip itself. The receiving side is shared and unit-tested (`translateSelection` is pure geometry over session state), so an iOS host that observes its own scrollers can adopt it unchanged |
24
24
| Recallable selection marks (a captured note's geometry comes back on hover) | shared: `AnnotationNote.anchorRect`/`drawnRect` snapshotted in `AnnotationSession.addNote`, `PinAttentionRule` + `RecalledMark` as pure rules, rendered by the shared `AnnotationMarks`| same | none — every part of it is platform-free. The two rects are WINDOW-LOCAL, which is the one place a platform could have crept in: macOS subtracts the overlay panel's `axOrigin`, iOS passes `.zero` because its view-tree frames are already view-local, and `addNote` takes that origin as a parameter rather than knowing which platform it is on. Both fields are UI-only and out of `CodingKeys`, so the exported record is identical on both |
25
-
| Pins inert in frame mode / hover-to-edit gated to point mode | shared `AnnotationPins` (`allowsHitTesting(session.tool == .point)`, and the same condition on the hover that opens the editor) | same | none in the code. ASYMMETRIC VERIFICATION, recorded rather than closed: `AnnotKitOverlayProbe` phase 11c posts a real press at a pin's own coordinates in each mode and is macOS-only, so on iOS the fix is covered only by the unit tests over the session and the rules. Note the hover half is largely moot on touch-only iOS — hover exists there only with a trackpad or pencil |
25
+
| Pin VIEW inert in frame mode / hover-to-edit gated to point mode | shared `AnnotationPins` (`allowsHitTesting(session.tool == .point)`, and the same condition on the hover that opens the editor) | same | none in the code. ASYMMETRIC VERIFICATION, recorded rather than closed: `AnnotKitOverlayProbe` phase 11c posts a real press at a pin's own coordinates in each mode and is macOS-only, so on iOS the fix is covered only by the unit tests over the session and the rules. Note the hover half is largely moot on touch-only iOS — hover exists there only with a trackpad or pencil |
26
+
| Pin CLICK-TO-EDIT in both tools (VRT-u209) | shared: `SelectionGesture.resolve(…pins:)` consults `PinAttentionRule.pressedNote` on a press that did not travel, and the shared catcher in `OverlayView` opens the editor for `.editNote`| same | none — the whole decision is a pure rule over the notes' stored window-local anchors, so both hosts route a tap identically, and iOS gets it from the one catcher it already shares. The TRAVEL gate is the only platform-sensitive part and it is already parameterized (`SelectionGesture.minimumTravel` is 10 on iOS vs 6 on macOS, because a finger rolls several points on a deliberate tap) — so a touch that would have been a sloppy tap still opens the pin's editor rather than being read as a drag. Verification is the same asymmetry as the row above: probe 11c is macOS-only; iOS relies on the shared unit tests |
26
27
| Recall survives a scroll |`KeyablePanel.scrollWheel` measures the translation it applied to the host's `NSScrollView` and feeds `AnnotationSession.translateNotes(by:within:)`| none | ASYMMETRIC BY THE MECHANISM: the correction is affordable on macOS only because the overlay panel already intercepts every wheel event and drives the clip itself, so the exact translation is known at the moment it is applied. iOS does not intercept `UIScrollView`, so nothing there knows a scroll happened; a note's rects stay where they were captured, which is the pre-existing behaviour `AnnotationPins` has always accepted for the pin. The RECEIVING side is shared and unit-tested (`translateNotes` is pure bookkeeping over `pending`), so an iOS host that later observes its own scrollers can adopt it without re-deriving anything |
27
28
| Overlay excluded from element lookup | AX window identifier (`AXIntrospection.overlayWindowIdentifier`) filtered out of every `kAXWindows` read |`PassThroughWindow` TYPE identity filtered out of `IOSElementSource.windows()`| ASYMMETRIC BY NECESSITY — the hosts are different window kinds. macOS's overlay is a separate `NSPanel` matched by the identifier the controller stamps on it; iOS's is a `UIWindow` in the HOST's scene sharing its pid, so no pid/scene filter separates it and a type check (internal to the module) cannot drift the way an identifier convention can. Both filter in the shared window lookup, so snapshot / hit-test / region-anchor / marquee agree; leaving it in would let a marquee bind the user's note to AnnotKit's own UI |
28
29
| Coordinate space | Cocoa bottom-left to AX top-left flip | UIKit top-left native | iOS needs no flip; shared `ScreenSpace` used only on macOS |
0 commit comments