feat(map): Follow & Auto-zoom on Map Analysis (#3788 Phase 2) - #4012
Conversation
…Paused (#3788 P2 WP-A) Foundation for Phase 2 Follow/Auto-zoom: persisted followMode/autoZoom flags on MapAnalysisConfig (mirroring selectedNodeIds), hardened load() coercion for old/garbage config, and transient followPaused state on MapAnalysisContext for the not-yet-built follow controller.
…WP-B) Leaflet-free averageLatLng + planAutoZoom (empty/single/coincident/ multi with 15% pad) for the upcoming FollowController; kept pure and unit-tested so the branching isn't buried in a Leaflet effect.
…updates (#3788 P2 WP-C) Add the `useMap()` view controller (spec §3c) that recenters/fits the map onto the selected nodes' current positions each live update, and wire it into the MapAnalysisCanvas `<MapContainer>` (§3e). Toolbar toggles and the Resume overlay are WP-D and intentionally excluded here. - FollowController.tsx: selected-points/`sig`/`selKey` derivation; the `programmaticRef` + `animate:false` + rAF safety-net lifecycle so our own moves don't self-pause; single `moveend` gate that pauses on genuine user pan/zoom; apply effect (Follow / Auto-zoom / both-on precedence, epsilon no-op guard); pause auto-reset on selection membership and mode toggles (never on position drift). - Wire `<FollowController/>` after `<TileLayer/>` inside `<MapContainer>`. - MapAnalysisCanvas.test.tsx: mock FollowController as a no-op there (its behavior is covered by the dedicated suite) so the existing `useMap()->null` spiderfier path is preserved. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015AFBA76hsjqhsXe1BdnYub
…rlay (#3788 P2 WP-D) Adds the Follow/Auto-zoom toggle buttons to MapAnalysisToolbar and a FollowResumeButton overlay (sibling of MapContainer, alongside MapLegend) that surfaces "Resume follow" only while a mode is active and paused, clearing the pause on click. Extends toolbar tests for the new toggles.
…se 2 spec (#3788) - User docs: map-analysis.md gains a "Follow & Auto-zoom" section, a toolbar row, and a persistence-list mention. - Internal: epic plan Phase 2 marked done with browser-validation evidence; commit the Phase 2 implementation spec. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015AFBA76hsjqhsXe1BdnYub
|
Claude finished @Yeraze's task —— View job PR Review: feat(map): Follow & Auto-zoom on Map Analysis (#3788 Phase 2)Tasks:
This is a well-structured, clean feature implementation. The architecture decisions are sound and the test coverage is thorough. Below are findings ranging from critical issues to minor suggestions. Potential Bug: Race Condition in
|
Epic #3788 — "Follow nodes on the map", Phase 2 of 2 (builds on Phase 1's node selection, merged in #4010). Plan:
docs/internal/dev-notes/MAP_FOLLOW_AUTOZOOM_EPIC.md.What
Adds Follow and Auto-zoom toggles to the
/analysistoolbar that keep the selected nodes framed as their positions update (15s cross-source poll):fitBoundsof the selection +15% margin. When both on, Auto-zoom governs.How (frontend-only, builds on Phase 1)
followMath.ts(new) — pure, leaflet-freeaverageLatLng+planAutoZoom(discriminatednone/single/multiunion encoding empty/single/coincident + the 15% pad). All edge-case branching is here and unit-tested.FollowController.tsx(new) —useMap()child. Derives the selected points from Phase 1'suseAnalysisNodes()/selectedNodeIds(shared query cache, no refetch). Applies Follow/Auto-zoom on each position-signature change. Manual-pan detection: a singlemoveendgate +programmaticRefflag +{animate:false}(programmaticmoveendfires synchronously and is consumed) + arequestAnimationFramesafety-net for no-op moves. Pause auto-resets on selection membership change or toggle change — never on position drift (so the 15s poll can't silently un-pause).FollowResumeButton.tsx(new) — overlay shown only while a mode is active and paused.followMode/autoZoomadded toMapAnalysisConfig(persisted, load-safe);followPausedis transient context state (not persisted).Tests
New/extended:
followMath.test.ts,FollowController.test.tsx(all Follow/Auto-zoom/both/single/empty cases + programmatic-move-doesn't-self-pause + user-move-pauses + paused-inert + no-op guard),FollowResumeButton.test.tsx,useMapAnalysisConfig.test.ts,MapAnalysisToolbar.test.tsx. Full suite: 2744/2744 suites, 0 failures; typecheck +lint:ciclean.Browser-validated on the deployed build
Follow recentered
[25,-95]z9 → [25.978,-80.137]z9(selection average, zoom preserved); Auto-zoom fit a wide select-all fromz14 → z0; a near-coincident pair correctly stayed put (single-case, no zoom-to-max); a user zoom paused + surfaced "Resume follow" + did not yank back; Resume re-engaged (refit to z0); toggles persisted across reload. No feature console errors.Closes #3788 (completes the epic: Phase 1 selection #4010 + this).
🤖 Generated with Claude Code
https://claude.ai/code/session_015AFBA76hsjqhsXe1BdnYub