Skip to content

#208 PR4c: centralize the point/cluster mode decision (filtersForcePoint + computeTargetMode)#301

Merged
rdhyee merged 1 commit into
isamplesorg:mainfrom
rdhyee:refactor/208-computeTargetMode
Jun 19, 2026
Merged

#208 PR4c: centralize the point/cluster mode decision (filtersForcePoint + computeTargetMode)#301
rdhyee merged 1 commit into
isamplesorg:mainfrom
rdhyee:refactor/208-computeTargetMode

Conversation

@rdhyee

@rdhyee rdhyee commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Continues the #249/#208 strangler refactor (after PR4a writeGlobeHash #288 and PR4b reconcileSettledCamera #289). Behavior-neutral — no functional change; this is the seam that makes the upcoming #300 feature a one-place edit.

Problem

The point/cluster mode decision was duplicated across four sites, with the predicate searchIsActive() || hasFacetFilters() copy-pasted at each:

  • camera.changed targetMode expression (+ its inner re-check)
  • applySearchFilterChange's forcePoint
  • the deep-link restore's wantsPoint

Changing the rule (as #300 needs) would mean a coordinated 4-site edit on the delicate mode state machine.

Change

Two helpers in the zoomWatcher cell, next to the existing getMode()/searchIsActive() consts:

  • filtersForcePoint() — the single "a filter forces point mode" predicate
  • computeTargetMode(alt) — the altitude-hysteresis authority, wrapping camera.changed's exact expression

camera.changed routes through computeTargetMode; the other three sites call filtersForcePoint() for the filter half and keep their exact altitude comparisons (>= vs >, ENTER vs EXIT). handleFacetFilterChange is intentionally left untouched — it uses hasFacetFilters()/searchIsActive() separately, not the combined predicate, so substituting there would not be neutral.

getMode() is called in computeTargetMode's tail branch (not threaded as a param) so it evaluates only in the hysteresis band — exactly as the original inline expression did.

Why (#300)

This centralizes where #300 will relax the facet case above EXIT_POINT_ALT to render filtered H3 clusters instead of forcing point mode.

Verification

  • Unit: node --test tests/unit/*.test.mjs — 13/13
  • ojs render OK (quarto render explorer.qmd)
  • [data] Playwright explorer-characterization + url-roundtrip against a live preview — 12/13; the single failure (facet hydration source counts) was a cold-cache data-load flake that re-passed in 8s. All mode/URL specs green.
  • Codex review: behavior-neutral, no behavior-changing issues.

🤖 Generated with Claude Code

…() (behavior-neutral)

The point/cluster mode decision was duplicated across four sites: the
camera.changed targetMode expression, applySearchFilterChange's forcePoint,
the deep-link restore's wantsPoint, plus the re-check inside camera.changed.
The forced-point predicate `searchIsActive() || hasFacetFilters()` appeared
verbatim at each.

Centralize into two helpers next to the existing getMode()/searchIsActive()
consts in the zoomWatcher cell:
  - filtersForcePoint() — the single "a filter forces point mode" predicate
  - computeTargetMode({alt, currentMode}) — the altitude hysteresis authority,
    wrapping camera.changed's exact expression

This is the seam isamplesorg#300 needs: relaxing the FACET case above EXIT_POINT_ALT to
filtered clusters becomes a one-place change here instead of a 4-site edit.

Behavior-neutral: every substitution is expression-identical. camera.changed
routes through computeTargetMode (same expr); the other three sites call
filtersForcePoint() for the filter half and keep their exact altitude
comparisons (>= vs >, ENTER vs EXIT). handleFacetFilterChange is intentionally
untouched — it uses hasFacetFilters()/searchIsActive() separately, not the
combined predicate.

Gate: unit 13/13; ojs render OK; characterization [data] 12/13 first pass with
the 1 failure (facet hydration) a cold-cache data-load flake that re-passed in
8s; all mode/URL specs green.

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