mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-05-25 13:44:05 +00:00
2f0c97604b
## Summary Implements map marker clustering for large meshes (500+ nodes) using vendored `Leaflet.markercluster@1.5.3`. Closes the long-standing no-op `Show clusters` checkbox. ## What changed **Vendored library** — `public/vendor/leaflet.markercluster.js` + `MarkerCluster.css` + `MarkerCluster.Default.css`. No CDN: this runs offline on mesh-operator deployments. **`map.js`** - `createClusterGroup()` instantiates `L.markerClusterGroup` with: - `chunkedLoading: true` (no frame drops on initial render) - `removeOutsideVisibleBounds: true` (viewport culling — key win at 2k+ nodes) - `disableClusteringAtZoom: 16` (fully expanded at high zoom) - `spiderfyOnMaxZoom: true` (fan out at max zoom) - `showCoverageOnHover: false` - `animate` disabled on mobile UA for perf - `makeClusterIcon(cluster)` produces a CoreScope-themed `L.divIcon`: - Bold total count, centered - Up to 4 role-color mini-pills (repeater / companion / room / sensor / observer) using `ROLE_COLORS` - Bucketed `mc-sm` / `mc-md` / `mc-lg` background (info / warning / accent CSS vars) - `#mcClusters` checkbox repurposed from no-op `Show clusters` → `Cluster markers`, default **ON**, persisted to `localStorage['meshcore-map-clustering']` - Render branches at the marker-add step: clustering ON → `addLayers()` to `clusterGroup`, skip `deconflictLabels` + `_updateOffsetIndicator` polylines + `_repositionMarkers` on zoom/resize. Clustering OFF → original flow unchanged. - Route polylines (`drawPacketRoute`) already removed both layers — no change needed beyond actually instantiating `clusterGroup`. - `?node=PUBKEY` deep-link lookup now searches both `markerLayer` and `clusterGroup` so it works in either mode. **`style.css`** — cluster bubble + role-pill styles using `--info` / `--warning` / `--accent` CSS variables; hover scale. **`index.html`** — vendor CSS + JS tags after the Leaflet bundle (cache-busted via `__BUST__`). ## TDD - **Red commit** `e10af23` — `test-map-clustering.js` + stub `createClusterGroup`/`makeClusterIcon` returning null/empty divIcon. Compiles, runs, fails 4/5 on assertions. - **Green commit** `482ea2e` — real implementation. 5/5 pass. ``` === map.js: clustering === ✅ exposes test hooks (__meshcoreMapInternals) ✅ createClusterGroup returns an L.MarkerClusterGroup with required options ✅ cluster group accepts markers via addLayer ✅ makeClusterIcon: includes total count and role-pill counts ✅ makeClusterIcon: bucket sm/md/lg by total ``` ## Behavior preserved - Clustering OFF (existing checkbox unchecked) → all original behavior intact: deconfliction spiral, offset-indicator polylines, per-zoom reposition. - Default ON. Operators with small meshes can disable via the checkbox; choice persists. - Spiderfying enabled at max zoom (built-in markercluster behavior). ## Performance target Smooth pan/zoom at 2000 nodes — `chunkedLoading` keeps the main thread responsive during initial add, `removeOutsideVisibleBounds` keeps DOM bounded to the viewport. Per AGENTS.md rule 0: complexity is O(n) for the initial add (chunked across frames), per-zoom re-cluster is internal to markercluster (well-tested at 10k+ scale). ## Out of scope (filed as follow-ups in spec) - Canvas marker renderer — only if 5k+ nodes per viewport materializes - Server-side viewport culling (`/api/nodes?bbox=`) - Cluster-by-role split groups - 2k-node fixture + Playwright DOM assertions — repo doesn't currently ship a `fixture=` query param; the unit test exercises the integration deterministically. Fixes #1036 --------- Co-authored-by: corescope-bot <bot@corescope>
15 lines
872 B
CSS
15 lines
872 B
CSS
.leaflet-cluster-anim .leaflet-marker-icon, .leaflet-cluster-anim .leaflet-marker-shadow {
|
|
-webkit-transition: -webkit-transform 0.3s ease-out, opacity 0.3s ease-in;
|
|
-moz-transition: -moz-transform 0.3s ease-out, opacity 0.3s ease-in;
|
|
-o-transition: -o-transform 0.3s ease-out, opacity 0.3s ease-in;
|
|
transition: transform 0.3s ease-out, opacity 0.3s ease-in;
|
|
}
|
|
|
|
.leaflet-cluster-spider-leg {
|
|
/* stroke-dashoffset (duration and function) should match with leaflet-marker-icon transform in order to track it exactly */
|
|
-webkit-transition: -webkit-stroke-dashoffset 0.3s ease-out, -webkit-stroke-opacity 0.3s ease-in;
|
|
-moz-transition: -moz-stroke-dashoffset 0.3s ease-out, -moz-stroke-opacity 0.3s ease-in;
|
|
-o-transition: -o-stroke-dashoffset 0.3s ease-out, -o-stroke-opacity 0.3s ease-in;
|
|
transition: stroke-dashoffset 0.3s ease-out, stroke-opacity 0.3s ease-in;
|
|
}
|