Skip to content

#208 PR4b: shared settled-camera tail (reconcileSettledCamera)#14

Open
rdhyee wants to merge 1 commit into
mainfrom
refactor/pr4b-reconcile-settled-camera
Open

#208 PR4b: shared settled-camera tail (reconcileSettledCamera)#14
rdhyee wants to merge 1 commit into
mainfrom
refactor/pr4b-reconcile-settled-camera

Conversation

@rdhyee

@rdhyee rdhyee commented Jun 16, 2026

Copy link
Copy Markdown
Owner

isamplesorg#208 PR4b — shared settled-camera tail (reconcileSettledCamera())

Continues the isamplesorg#249 strangler refactor (PR4a centralized hash writes behind writeGlobeHash). Targets isamplesorg#208 smell 1b: a single settled-camera reconciliation entry point shared by both globe listeners. Branch off main (fork staging); promote upstream later like isamplesorg#287/isamplesorg#288.

What

Extracts the shared tail both camera.changed and moveEnd run once the camera settles — the cluster "Samples in View" stat refresh + the URL-hash write — into a local reconcileSettledCamera(v), called from both.

This gives moveEnd the same cluster-stat refresh camera.changed already did, closing the sub-10%-pan gap: a small cluster-mode drag fires moveEnd (which kept the URL fresh via isamplesorg#204) but not camera.changed (debounced away by percentageChanged=0.1), so the "Samples in View" count went stale until a larger move.

Scope (deliberately minimal — Codex Q3 / REFACTOR_PR4_PLAN.md §3)

Behavior

  • camera.changed: behavior-neutral — same order (cluster-stat then hash), early pre-await write intact.
  • moveEnd: adds the cluster-stat refresh. Point mode skips it via the getMode()==='cluster' guard (unchanged there). The stat read is synchronous, guarded by _clusterData, and writes no mode/selection/URL/facet/heatmap state.

Verification

  • smoke 4 + characterization 7 + url-roundtrip 5 all green (behavior-neutral URL contract from both handlers — incl. explorer: add camera.moveEnd URL-write backstop (closes #204) isamplesorg/isamplesorg.github.io#205 sub-threshold moveEnd + cross-context round-trip).
  • quarto render explorer.qmd clean.
  • ✅ Codex review of the diff: no blocking findings (confirmed camera.changed neutral, no boot/flight hazard from the moveEnd cluster-stat, ordering safe, satisfies explorer: refactor URL/mode state management (centralize writers, collapse dual mode) isamplesorg/isamplesorg.github.io#208 smell 1b as scoped).
  • ⚠️ A dedicated headless regression for the moveEnd cluster-stat refresh proved unreliable — the explorer OJS cell re-evaluates during headless boot, yielding multiple viewer instances; the one reachable via value('viewer') at interaction time often has no camera listeners, so a forced moveEnd never reaches the handler. Rather than ship a flaky test, the rationale is documented inline in url-roundtrip.spec.js. A manual probe confirms a settled cluster camera writes <count> | Samples in View via the shared tail.

🤖 Generated with Claude Code

…Camera()

Extract the shared settled-camera reconciliation both globe listeners run
once the camera comes to rest — the cluster "Samples in View" stat refresh
+ the URL-hash write (writeGlobeHash) — into reconcileSettledCamera(v), and
call it from BOTH camera.changed and moveEnd (isamplesorg#208 smell 1b).

This gives moveEnd the same cluster-stat refresh camera.changed already did,
closing the sub-10%-pan gap: a small cluster-mode drag fired moveEnd (which
updated the URL via isamplesorg#204) but NOT camera.changed (debounced away by
percentageChanged=0.1), leaving the "Samples in View" count stale.

Scope is deliberately minimal (Codex Q3 / REFACTOR_PR4_PLAN.md §3): NOT the
full handler merge. Mode-transition + resolution-reload stays camera.changed-
only; facet/heatmap/point-exit stays moveEnd-only; isamplesorg#262 stays a separate
tracked sibling. reconcileSettledCamera is a local fn (closes over
getMode/currentRes/countInViewport), not top-level like writeGlobeHash.

Behavior:
- camera.changed: behavior-neutral (same order — cluster-stat then hash).
- moveEnd: adds the cluster-stat refresh (point mode skips it via the
  getMode()==='cluster' guard, so point mode is unchanged). The stat read is
  synchronous, guarded by _clusterData, and writes no mode/selection/URL/
  facet/heatmap state.

Verification: smoke 4 + characterization 7 + url-roundtrip 5 all green
(behavior-neutral URL contract from both handlers preserved); render clean;
Codex review of the diff found no blocking issues. A dedicated headless
regression for the moveEnd cluster-stat refresh proved unreliable (OJS cell
re-evaluation yields multiple viewer instances in the harness; the one
reachable at interaction time often has no camera listeners) — documented
inline in url-roundtrip.spec.js rather than shipped flaky.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant