Commit Graph
2 Commits
Author SHA1 Message Date
bf8bb87286 fix(live): canvas-anim cleanup carryover from #1490 (#1514) (#1520)
# Canvas-anim cleanup — follow-up to #1490

Fixes #1514. Addresses ALL items from the issue checklist (M1, M2,
S1–S10) in 7 logically grouped commits.

## Summary by category

### Must-fix
- **M1** — DPR listener self-rebind in a `try/finally` replaced with a
`{once: true}` MQL pattern. The runtime drops the listener atomically
before our handler runs, so re-binding is race-free; a thrown
`updateAnimCanvas()` no longer leaves a half-bound listener. Comment
documents the strict-match limitation of `matchMedia('(resolution:
Xdppx)')` (S10).
- **M2** — Stale `// Uncomment if you created the custom pane in the
previous step` comments removed. Fading polylines now render on
`animationsPane` (z=625) for consistent stacking with the moving phase:
above markers, below tooltips/popups. **Design choice:** the recommended
option (uncomment) was taken — fades are short-lived and capped at 5
recent paths, so marker-overlap is not a concern.

### Should-fix
- **S1** — 85 lines of whitespace-only churn from #1490 reverted
(`function ()` ↔ `function()`, `'0':0x7E` ↔ `'0': 0x7E`, etc.). Net
behavioral change: zero. Done as its own commit so reviewers can verify
it's purely cosmetic.
- **S2** — `renderAnimations()` per-frame allocations (`fromPt`, `toPt`)
hoisted to module-scoped `_scratchFrom` / `_scratchTo` reused each
frame. Saves ~6000 garbage objects/sec at 50 anims × 60fps.
- **S3** — `destroy()` now drains `onComplete` callbacks BEFORE clearing
`activeAnimations`. Audio `onHop` hooks no longer dropped on navigation.
- **S4** — Duplicate `window._liveTestSeams` definition deleted. Single
source of truth at the earlier exposure block (uses production
`wakeCanvasEngine` which respects pause/empty-queue guards).
- **S5** — E2E synthetic packet count bumped from 5 to 20 so the
`recentPaths.length > 5` prune actually executes.
- **S6** — E2E canvas selector pinned to
`.leaflet-pane.leaflet-animations-pane canvas` so it can't accidentally
match Leaflet's own `preferCanvas:true` renderer on overlayPane.
- **S7** — Z-index architecture comment now documents BOTH
`animationsPane` (z=625) and `liveAnimPane` (z=650) with rationale + a
pointer to the out-of-scope migration of the remaining SVG paths.
- **S8** — `destroy()` consolidated into one ordered teardown (drain →
stop loops → cancel timers → tear down canvas before `map.remove()` →
reset module state). Inline comments explain ordering.
- **S9** — `evenSize()` JSDoc with cross-link to `live.css:~1300`
("Eliminate SVG baseline drift") so the relationship between SVG marker
pixel snapping and even DOM sizes is discoverable from either side.
- **S10** — Subsumed by M1: the new DPR rebind comment explains the
strict-match limitation and the rebind handles transitions.

## Hot-load + visual QA

Hot-loaded via `scp` + `docker cp` to the staging runner's
`corescope-staging-go` container at `/app/public/live.js` and verified
the staging live map at <http://analyzer-stg.00id.net/#/live> with the
local headless chromium tool (CDP):

- Both `animations-pane` (z=625) and `liveAnim-pane` (z=650) present in
the rendered DOM.
- After firing 6 synthetic packets, animations-pane held 2 canvases
(anim canvas + Leaflet's polyline canvas renderer for the fades) and
`overlay-pane` had 0 polyline paths — confirming M2 routes fades to the
correct pane.
- `_liveTestSeams.{getAnimCount,isAnimating,getPathCount,wake}` all
functional via the now-singleton seam (S4).
- After visual QA, staging restored to tip-of-master (auto-deploy on
merge will re-deploy this branch's content).

Screenshot of the live map on staging with the patched `live.js`
hot-loaded was captured locally during QA (sandbox-internal path; cannot
attach to GitHub from worker context).

## E2E runs (sandbox limitation noted)

The sandbox running this work is the same kind of constrained ARM-ish
box that AGENTS.md flags ("Heavy coverage collection scripts may crash —
use CI for those"). On this hardware, the **unmodified master version of
`test-pr-1490-live-map-gpu-animations-e2e.js` failed 0/10** times due to
the 1500ms 2× drain timeout being insufficient for chromium-headless
under sandbox load (page load alone is ~3.7s vs ~700ms on CI). The test
passes on CI runners where #1490 went green.

What I verified locally:
- `node test-live-anims.js` — **9/9 + 5/5 passed, 5 consecutive runs**
(the unit test sniffs source for the canvas engine seams, including
`_liveTestSeams.wake` after S4 dedup).
- Full `bash test-all.sh` shows no NEW failures vs master baseline (30
pre-existing failures around `AreaFilter is not defined` in
`test-frontend-helpers.js` — unrelated).
- `bash ~/.openclaw/skills/pr-preflight/scripts/run-all.sh
origin/master` — **exit 0** (clean).

I did NOT bump the 1500ms drain timeout. Step 5's one-shot `isAnimating
=== false` check was changed to a 200ms `expect.poll` because there is a
single rAF tick between `activeAnimations.length` going to 0 and the
next renderAnimations frame setting `isAnimating = false`; the original
one-shot raced that frame. 200ms is the smallest jitter buffer for one
rAF tick (~16ms × headroom for slow CI), not a generic timeout bump.

CI is the source of truth for the 20× pass requirement. If CI's first
run is flaky on this test, file as a follow-up — the underlying race
(1-frame settle delay between `getAnimCount==0` and
`isAnimating==false`) is what the `expect.poll` change addresses.

## Preflight

```
bash ~/.openclaw/skills/pr-preflight/scripts/run-all.sh origin/master
═══ Preflight clean. ═══
```

Exit code: 0.

## Commits

```
b03f8fca docs(live): document dual animation panes + JSDoc evenSize() (#1514 S7+S9)
e2afc986 test(live): strengthen pr-1490 e2e — exact pane selector + 20 packets (#1514 S5+S6)
a568c361 refactor(live): dedupe _liveTestSeams and consolidate destroy() (#1514 S4+S8)
498a2dcb perf(live): hoist scratch points + drain onComplete on destroy (#1514 S2+S3)
6d5d4394 fix(live): place fading polylines on animationsPane for consistent z-stacking (#1514 M2)
0d32f063 fix(live): replace fragile DPR listener self-rebind with race-free pattern (#1514 M1)
976ccf6d style(live): revert auto-format whitespace churn from #1490 (#1514 S1)
```

---------

Co-authored-by: OpenClaw Bot <bot@openclaw.local>
Co-authored-by: mc-bot <bot@meshcore.local>
2026-05-31 19:53:55 -07:00
Eldoon NemarandKpaBap 914f869421 Make packet movement on the live map hardware-accelerated using HTML5 (#1490)
Instead of forcing Leaflet to recalculate and paint heavy SVG DOM nodes
60 times a second for every moving packet, we will draw the flying dots
and lines directly onto a hardware-accelerated HTML5 <canvas> overlaid
on the map. Once the animation finishes, it will drop a static Leaflet
line to handle the fading tail effect.

---------

Co-authored-by: KpaBap <kpabap@gmail.com>
2026-05-31 20:54:11 +00:00